Skip to main content

Why enrichers

Customer.io, Braze, Iterable. Different visual canvases; same backbone. Each runs on a copy of your customer data — synced from your warehouse, your CRM, your application database, into the platform’s own database. The sync is permanent. The drift is permanent. The privacy surface is permanent. We think that’s a structural mistake the entire category inherited from CRM-era tools. Notifizz doesn’t sync.

What we believe

Customer data should stay where it lives. The notification platform should fetch what it needs at the moment it sends, from the source of truth, with no copy. We call this primitive an enricher — a server-side function the orchestrator calls at notification time. It’s the difference between a permanent ETL pipeline and a one-line API call.

The status quo

Every major marketing automation platform is a warehouse-sync platform under the hood:
PlatformWhere customer data livesHow it gets there
Customer.ioCustomer.io databaseReverse ETL from your warehouse / API push from your app
BrazeBraze databaseReverse ETL from your warehouse / API push
IterableIterable databaseReverse ETL from your warehouse / catalogue uploads
The pattern is identical. Your data lives in N places (warehouse, CRM, application DB, Braze). The notification platform reads from its copy. To keep the copy fresh, an integration runs continuously — Hightouch, Census, your own ETL, the platform’s import API. Three problems compound.

1. Drift

Last night’s sync ran. This morning, the user changed their email. The campaign fires. The platform sees the old email; the user gets the notification on an address they don’t read anymore. Drift is structural, not configurable — it’s the gap between when the sync ran and when the notification fired.

2. Privacy and data minimisation

GDPR Article 5 says you should hold customer data only for as long as you need it. Warehouse-sync platforms hold a copy permanently — for every customer who might receive a notification, regardless of whether one ever fires. The DPO has to audit a separate database, evaluate a separate vendor, and sign a separate DPA, all for a copy of data they already audit in your warehouse.

3. Cost and complexity

The sync pipeline is its own engineering surface. New customer attribute? Schema migration in the platform + ETL job update. Rename a field? Coordinated change across two systems. Deprecate a field? Hope nothing in the platform still reads it. Reverse ETL vendors exist as a category specifically because this is hard.

The Notifizz alternative

Notifizz holds no customer data. Recipient identifiers (userId, email) ride on the event. Anything else the campaign needs comes from an enricher — a function you register in the Node SDK that fetches live data from your systems.
client.enricher("fetchUser", {
  input: z.object({ userId: z.string() }),
  output: z.object({ id: z.string(), email: z.string(), plan: z.string() }),
  cache: { ttl: "1h" },
  handler: async ({ userId }) => {
    const user = await db.users.findOne({ id: userId });
    return { id: user.id, email: user.email, plan: user.plan };
  },
});
The orchestrator calls enrichWith("fetchUser", { userId }) at notification time. Notifizz POSTs your endpoint with HMAC-signed headers; your service returns the live record. The campaign uses what came back. Three benefits — each the inverse of a status-quo problem.

Real-time consistency

The user’s email is whatever your DB says it is at notification time. There’s no sync window, no nightly batch, no drift. Update propagation is the latency of one HTTP call (typically sub-100ms with a cache hit).

GDPR / data minimisation

Notifizz processes the customer data, doesn’t store it. The privacy surface is the event payload (which you control) and the per-message snapshot (delivery history). The customer data graph stays in your systems where your DPO already audits it. Adding Notifizz doesn’t add a copy to evaluate.

Simplicity

No sync pipeline. New attribute the campaign needs? Add a field to the enricher’s output schema; deploy. Rename? Update one file. Deprecate? Delete the field. The notification platform consumes your live data the same way the rest of your application does. The protocol — discovery, HMAC, anti-replay, cache policy — is documented in enrichers protocol. The end-to-end tutorial is in enrichers tutorial.

Caching is not sync

The objection writes itself: “but you cache enricher responses — isn’t that a sync?” No. The differences matter:
Sync (warehouse-sync platforms)Cache (Notifizz enrichers)
Pre-fetches data before any notification firesFetches data only when a notification fires that needs it
Stores every customer in the platform’s DBStores only the records used recently, evicted by TTL
Holds the data indefinitelyEvicts on TTL or on cache invalidation
Privacy surface = full customer databasePrivacy surface = the records actively used in the last TTL window
New attribute requires schema migrationNew attribute is a field in the enricher’s response
A cache is an optimisation over the source of truth; a sync is a parallel source of truth. The data lifecycle is different, the privacy posture is different, the engineering surface is different.

When sync would actually beat enrichers

Two cases where sync’s a defensible choice:
  1. Massive batch campaigns where the cost of N enricher calls exceeds the cost of a sync. A weekly newsletter to 5M subscribers may benefit from a pre-fetched audience. We’re aware of the tradeoff; the platform’s batch dispatcher can be tuned, and most teams that hit this scale have a hybrid pattern (sync the audience definition, enrich the per-recipient personalisation).
  2. Audiences computed from heavy joins your DB can’t serve at notification-time speed. If your “users at risk of churn” requires three minutes of warehouse SQL, you can’t compute it on a hot path. The pattern: precompute the audience nightly into a saved dynamic recipient source, trigger a campaign on it. The sync still happens — but for the audience definition, not the customer attributes.
For everything else — the 99% case of “send transactional + marketing notifications to customers based on their current state” — enrichers strictly dominate.

But what about…

Enrichers run against your operational systems (application DB, internal API), not against the warehouse. The cache policy means a popular enricher hits your DB at most once per TTL window per (orgId, name, paramsHash). For the typical pattern — a fetchUser cached 1h — your DB sees a few requests per second per popular user, not one per notification. The latency budget is sub-second.
Cold cache: one HTTP round-trip to your service plus your handler’s work. Most teams see 50-200ms. Hot cache: zero — the orchestrator gets the cached value, your service isn’t called. End-to-end (event acceptance → bell update) typically lands sub-second on healthy infrastructure.
Yes. The orchestrator’s recipient list iterates per recipient; the enricher’s response can resolve recipient details (email, locale, …) per-recipient via cache. For large fan-outs, the dominant cost is per-message dispatch, not enricher calls — enrichers cache aggressively and reuse across recipients sharing the same lookup keys.
Register one enricher per source. fetchUser from your DB, fetchSubscription from your billing system, fetchSegmentMembership from your audience tool. The orchestrator calls each as needed. The stitching happens server-side, in code you control — not in a vendor’s data model.
Customer.io’s webhook triggers a campaign — it accepts an inbound event. Notifizz enrichers enrich a notification — they’re outbound calls from the platform to your service, made at the moment the notification needs the data. Customer.io still runs on its synced copy of customer data; the webhook is just one way to start a journey on that data. Different layer of the architecture.
No. A cache holds the records you’ve actually used recently, evicted by TTL. A sync holds every customer, indefinitely, regardless of whether the platform ever uses them. The privacy surface, the cost, and the freshness story are all different. See “Caching is not sync” above.
Notifizz retries with backoff; persistent failures surface in the workflow trace as a typed error. The campaign can branch on enricher failures — proceed with degraded data, fail gracefully, retry later. The failure surface is explicit; you choose the policy.

Where to go next

Marketing autonomy

The hero pillar — enrichers are one of four ingredients.

Enrichers tutorial

Register and mount your first enricher in five minutes.

Enrichers protocol

Wire format, HMAC, cache policy, error semantics.

Recipients

How enricher output composes with recipient resolution.