# Playbook: CDP Event Journeys
**School:** the Segment / Customer.io pattern โ the modern Customer Data Platform (CDP) event-spine model. Standardized `track`/`identify` events feed journey builders so that email, tasks, and ads all subscribe to one shared stream instead of each channel wiring its own bespoke integration. Philosophical parent: Lester Wunderman's "response is the only truth" (1961/1967) โ every touch must carry a trackable action, updated for a multi-channel, machine-to-machine era.
**Deliverable type:** `integration-runbook` for the event schema and field map, plus a `journey-map` for the resulting stage transitions.
---
## When to use this
- More than one downstream system (email, task queue, ad platform, product) needs to react to the same underlying behavior, and today each is wired separately (duplicated logic, drift between what "an email open" means in two different systems).
- You're consolidating multiple funnel/product/email sources into the Sovereign CRM and want one canonical event vocabulary rather than ad hoc per-integration field names.
- You want ad retargeting, email sequencing, and task creation to all key off the exact same trigger, guaranteeing consistency (a contact who converts stops appearing in retargeting AND stops receiving the nurture email, because both subscribe to the same `purchase_completed` event).
## When NOT to use this
- You have exactly one data source and one downstream consumer (e.g., only ZIION funnel forms feeding only the email engine) โ a full standardized event spine may be premature; a direct integration-runbook without the CDP abstraction layer is simpler and just as correct at that scale.
- The team can't commit to schema discipline (no event ships without a name and a contract) โ a CDP pattern with an undisciplined schema just becomes another source of inconsistent data, arguably worse than no spine at all.
---
## Step-by-step build
1. **Define the two event primitives.** `identify` (who is this โ resolves to the customer-master record) and `track` (what did they do โ a named event with a payload).
2. **Write the standardized event schema.** Every event type gets a name (`lead_created`, `email_opened`, `purchase_completed`, `product_usage_threshold_crossed`), a required payload shape, and the systems allowed to emit it.
3. **Enforce "no event without a schema."** A source system cannot emit an ad hoc event name โ it must map to one of the named types in the schema, or the schema gets extended deliberately (not silently).
4. **Build the field-mapping table.** Source system's native event/field names โ the standardized schema's names, with any transform noted (a required field for the `integration-runbook` rubric).
5. **Reference auth via env var/vault only** for every source integration (funnel webhook secrets, ad-platform API tokens, email-engine credentials) โ inline secrets hard-fail this deliverable type.
6. **State sync frequency/trigger per source** โ real-time webhook vs. batch sync, and what happens on a missed/failed delivery (failure/retry behavior, required field).
7. **Write the dedupe rule** for events that could double-fire (e.g., a webhook retried by the source system shouldn't create two `purchase_completed` records).
8. **Wire the journey builder layer.** Once the event stream is standardized, journey/automation logic subscribes to named events rather than to raw per-system webhooks โ this is the actual leverage: build the sequence logic once, let email/task/ad channels all subscribe.
9. **Document rollback.** If a bad event schema change ships, what's the procedure to revert without losing in-flight event data?
## Worked mini-example
Standardized event: `purchase_completed` โ payload `{contact_id, order_id, amount, product_sku, timestamp}`. Emitted by: Shopify webhook (via `integration-runbook` field map: Shopify's `order.created` โ standardized `purchase_completed`, `total_price` โ `amount`, transform: currency-string to numeric cents). Once this event lands in the spine, three downstream subscribers react without any new wiring: (1) the email engine ends the active nurture sequence for that contact (mandatory EXIT โ no more "you haven't bought yet" emails to someone who just bought), (2) the ad platform integration suppresses retargeting ads for that contact, (3) the RFM engine's Monetary/Frequency fields update on the next batch recompute. Auth: Shopify webhook secret referenced as `env.SHOPIFY_WEBHOOK_SECRET`. Dedupe: `order_id` is the idempotency key โ a retried webhook with the same `order_id` is discarded, not re-processed.
## How the verify gate applies
The `integration-runbook` linter requires the field-mapping table, hard-fails on any inline secret, and requires the dedupe rule and rollback procedure to be present. Because this playbook explicitly feeds multiple downstream automations, the companion `journey-map` needs its conflict-rule field populated precisely because of the fan-out risk here โ e.g., stating that `purchase_completed` firing an EXIT on the nurture sequence takes priority over any winback automation that might otherwise re-target the same contact in the same event cycle.