Currently taking on only 1 new client in Q4 2026.

Maczfit Customer.io data architecture | Meal-kit ecommerce

Maciej Turek 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. More than €50,000 saved per month.

Ownership: architecture and journey design by Maciej Turek; warehouse preparation and Customer.io wiring by Maczfit engineering.

1. Context

Maczfit is a Polish meal-kit and diet ecommerce business running Customer.io at scale. The engagement was Customer.io data architecture: how events, attributes, and Objects should be used when carts, orders, and packages move at high daily volume. It was not a greenfield ESP migration.

2. Starting problem

Billable profiles in Customer.io count people and Objects together. Modelling high-volume carts, orders, or packages as Objects would exhaust that limit in days and 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.

3. Maciej’s role

Independent consultant. Designed the event-based data model, the split of responsibility between the warehouse preparation layer and Customer.io, the first journey shapes (abandoned cart and unpaid order), and the implementation path. Maczfit’s engineering team implemented the architecture. First-person ownership of the design, not of the warehouse build or production flow configuration.

4. Existing stack

The ecommerce platform was 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 was the activation layer for email, push, and SMS. The design assumed a periodic refresh cadence measured in minutes, so intermediate status hops inside one window could appear only as the end state. That constraint shaped wait logic and exit conditions in the designed flows.

5. Data architecture

The rule was simple. Events carry history, triggers, and status changes for carts, orders, packages, checkout, payment, and delivery. Attributes hold current customer state and exit conditions. Objects are used only for durable, low-volume 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. Customer.io receives business-relevant events, not a copy of the ecommerce history.

6. Events and attributes

The designed 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 as the abandoned-cart journey trigger
  • An observed unpaid-order event as the unpaid-order journey trigger
  • Cart-state attributes on the profile (active cart reference and last-seen timing)
  • Order and payment state attributes used for exits
  • Light personalisation attributes (preference and location class fields)

Payload fields for messages and exits covered identifiers, status before and after, value, preference and location context, first delivery timing, and deep links. This describes the model shape, not a production event catalogue.

7. Migration / implementation approach

Not a migration from another ESP. The designed path had two phases for Maczfit engineering to execute. Phase 1: confirm which payload fields the warehouse could supply, agree an event naming convention, and build snapshot comparison, status-change log, and event outbox. Phase 2: send test events into Customer.io, attach profile attributes, build flows with wait and state checks, run edge-case tests, then configure sending subdomains and warmup. The blocking dependency was payload field availability. Validation of how warehouse timestamps behave on status-only changes, and a later decision on ecommerce-direct emission (v2), could run in parallel and did not block the architecture itself.

8. Lifecycle journeys

The first designed scenarios were abandoned cart and unpaid order. Both used the same pattern: an observed event as trigger, a wait window, a check of current state on the profile before any send, and hard exits for purchase, checkout in progress, cancelled or deleted carts, unpaid orders that become paid or cancelled, and carts without a client id. Reminders were designed to send only after a positive state check, never from the trigger alone. Maciej designed these journey shapes; this page does not claim he configured the live flows.

9. Deliverability

The architecture split transactional and marketing traffic. Order confirmation and payment links for unpaid orders were treated as transactional and must not depend on marketing opt-in. Abandoned-cart reminders, offers, and upsells were marketing and need consent under GDPR and Polish e-commerce rules; SMS needs its own channel consent. Designed practice: separate subdomains for transactional and marketing reputation, domain warmup before full-base sends, and immediate removal of hard bounces. A single promotional element can reclassify a message as marketing. No measured bounce or inbox rates are published for this engagement.

10. QA / governance

Designed checks before and after go-live: confirm payload fields in the warehouse, lock the event naming convention, verify outbox pending/sent/failed states and stable idempotency-key behaviour, and test edge cases. Edge cases called out in the design included fast status hops inside one refresh window, missing customer identifiers, deleted carts, duplicate emission, and carts with multiple transactions where exit must validate the matching transaction. Specified ongoing monitoring covered event volume, duplicates, entries, exits, sends, conversions, and billable-profile usage. No unpublished production incident list is claimed here.

11. Engineering collaboration

This is the core of the case. Maciej designed the model and the implementation path. Maczfit’s engineering team built the warehouse preparation layer and wired Customer.io. The consultant did not replace that team. No named engineering headcount or ticket volume is published here.

12. Constraints

People and Objects share one billable-profile ceiling, so high-volume commerce entities cannot be Objects. Warehouse refresh on a minutes-scale cadence means intermediate statuses inside one window may never emit as separate events. In v1, events for these scenarios are generated from snapshot comparison, not real-time emission from the ecommerce platform. Ecommerce-direct (v2) was a later option if timing or capture accuracy proved insufficient; this page does not claim v2 shipped.

13. Outcome

Result:

  • More than €50,000 saved per month after the architecture was implemented

Technical outcome documented in first person: an event-based model that keeps Customer.io as an activation layer, with Objects limited to durable entities and high-volume carts and orders carried as events and attributes. No client quote is published.

14. What changed

The data model rule changed: events for high-volume history, attributes for current state, Objects only selectively. Responsibility split: the warehouse prepares and deduplicates events in v1; Customer.io consumes ready events. This page does not claim that every item in the architecture was fully shipped, only the design, the engineering handoff, and the savings figure above.

15. Lessons / what did not work

  • Modelling carts, orders, or packages as Objects under a shared billable-profile cap
  • Treating Customer.io as an analytics warehouse of ecommerce history
  • Triggering reminders from the observed event alone, without a current-state check before send
  • Assuming every useful personalisation field is already available in the warehouse before the payload schema is confirmed
  • Assuming status-only record changes always bump a modified timestamp without validating that behaviour

Need Customer.io data architecture like this?

Most engagements start with a short call to review your workspace, volume, and billable-profile constraints. No pitch, no deck.

Book a Free 30-min Strategy Call →