The decision filter.
I would use the same sequence before recommending any specific Braze feature or integration pattern.
| Step | Question |
|---|---|
| 1 · Outcome | What customer or business outcome are we trying to create, and what must we protect? |
| 2 · Discovery | Which few facts could actually change the answer: source, identity, data authority, latency, scale, consent, failure behavior, ownership? |
| 3 · Recommendation | What is the simplest sound path given those constraints? |
| 4 · Trade-off | What do we gain, what do we accept, and what would make me change the design? |
| 5 · Proof | How will we test the full customer path, monitor failures and know who owns recovery? |
The brief.
A local model tests 18 synthetic scenarios using the same engine in the browser and automated tests. It does not connect to Braze or send messages.
Run the exercise →Waypoint is a mobile-first travel marketplace. Users can browse anonymously, create an account, save destinations, search flights, book trips and receive email, push and in-app messages.
The lifecycle team wants abandoned search reminders, price drop alerts, booking and pre-trip messages, post-trip review requests and personalized destination recommendations. Waypoint already has a Snowflake warehouse and a backend booking service. Braze will power orchestration and messaging.
Questions that can change the architecture
- Where does each business fact become authoritative?
- Which stable identifier represents the same customer across systems?
- Which use cases need seconds or minutes, and which can tolerate scheduled freshness?
- What volume and burst patterns should the integration survive?
- Which system owns consent, contactability and durable profile state?
- What happens when data is late, duplicated, rejected or contradictory?
Identity and data authority are different decisions.
“Who is this customer?” is not the same question as “which system owns this fact?” Waypoint needs one stable customer identity after authentication, but it does not need one platform to become the source of truth for every field.
| Question | Choice | Why |
|---|---|---|
| Primary person identifier | Canonical customer_id after login | Stable across email changes and backend systems |
| Current email | Account / profile system | Mutable contact data should not define identity |
| Booking status | Booking service | Server-authoritative transaction state |
| Loyalty / derived value | Warehouse or loyalty source | Durable or calculated state can have its own authority |
Email can change without the person becoming a new customer. A stable ID answers identity; per-field authority answers which system is allowed to update each fact.
Choose ingestion by where the truth originates.
Client behaviour that naturally occurs in the app or site should usually be captured by the SDK. Trusted backend outcomes should come from the backend. Large recurring warehouse truth can use a scheduled warehouse-oriented path when immediate latency is unnecessary.
| Data | Source | Transport | Reason |
|---|---|---|---|
| destination_viewed | Mobile / web client | SDK | Device interaction originates on the client |
| search_completed | Client + search context | SDK | Behavioural moment; near-real-time |
| booking_completed | Booking service | REST | Backend owns the transaction result |
| booking_cancelled | Booking service | REST | Must not depend on the app opening |
| preferred_airport | Customer profile / warehouse | Scheduled sync | Durable state, not a moment |
| lifetime_bookings | Warehouse | Scheduled sync | Derived historical attribute |
Example backend event
POST /users/track { "events": [{ "external_id": "cust_18427", "name": "booking_completed", "time": "2026-08-31T14:32:00Z", "properties": { "booking_id": "BKG_92118", "destination": "Paris" } }] }
The payload carries enough context for orchestration and validation without turning Braze into a second booking database.
Real-time welcome alongside a legacy nightly batch.
Assume Waypoint has a legacy customer system that can only export an overnight CSV, but Marketing wants a welcome message within the first few minutes of signup. The wrong starting point is “how do we make the nightly file real-time?”
What I would clarify first
- Where does signup actually occur?
- When is the account considered authoritative?
- Can the website, app or backend emit a real-time signal independently?
- Which fields truly need immediate freshness versus next-day freshness?
- If two paths touch the same field, which update wins and how is freshness protected?
Recommendation: introduce a separate real-time path only for the fact that requires low latency. Keep slower-moving profile data on the scheduled path. Define authority and freshness for anything both paths can update.
Trade-off: two paths add governance and reconciliation work, but forcing every field into a real-time integration would add complexity without business value.
Proof: measure signup timestamp → Braze receipt → intended profile → journey entry → channel eligibility → send. Test duplicate signup, delayed confirmation and stale batch overwrite scenarios.
Abandoned search: design the exit before the message.
A user searches Toronto → Paris but does not book. Waypoint wants a reminder after two hours. The obvious build is a delay then a message. The safer build asks whether the business outcome already occurred.
- Exit when booking_completed arrives for the relevant journey context
- Define re-entry / cooldown instead of allowing accidental overlap
- Suppress customers who are not eligible for the channel
- Prefer the most recent valid search if multiple searches occur
- Measure purchase conversion, drop-off and opt-out / fatigue signals
- Use a control group when the goal is incremental lift, not just post-click conversion
Scope of the local model: each customer + search ID is evaluated separately. A production journey still needs re-entry, cooldown and “most recent search” rules across separate searches; those broader controls are design requirements here.
A million-customer launch under a request limit.
Assume a time-sensitive access message must go to 1,000,000 customers at 10:00 AM and the integration path is constrained to 10,000 requests per second. Before proposing anything, I would clarify whether that limit is per request or recipient, whether requests can batch recipients, and what “at 10:00 AM” actually means.
| If one request = one customer | 1,000,000 ÷ 10,000 requests/sec = 100 seconds minimum ingestion time, before network variance, retries or send processing. |
|---|---|
| If data can be staged | Load audience and access data before 10:00 AM, then let the orchestration layer start the communication at the required business time. |
| If true sub-minute freshness is required | Revisit batching, endpoint behavior, architecture, rate limits and whether the business SLA is achievable rather than promising an impossible clock time. |
Recommendation: separate data readiness from send time wherever the business allows it. Pre-stage stable audience data, preserve only the genuinely time-sensitive trigger for 10:00 AM, and model retries, partial failure and monitoring before launch.
Trade-off: pre-staging is operationally safer but requires clear freshness rules. Real-time ingestion gives fresher data but increases rate limit, retry and recovery complexity.
Failure modes belong in the architecture.
| Failure | Customer risk | Control |
|---|---|---|
| Booking event delayed | Reminder arrives after purchase | Exit logic, timing tests, source latency monitoring |
| Duplicate event | Duplicate downstream behavior | Stable business key + dedupe / idempotency pattern |
| Identity mismatch | Data splits across profiles | Canonical ID contract + cross-source test users |
| Warehouse attribute stale | Old tier / preference used | Field-level freshness rule + fallback behavior |
| Consent conflict | Ineligible message | Channel eligibility validation before delivery |
| Partial high-volume failure | Silent missing cohort | Count reconciliation, retry policy, alerting and named recovery owner |
Test the customer path, not just the component.
I would create named synthetic users covering identity, consent, timing, failure and re-entry scenarios. Then I would trace evidence from source through customer outcome.
Observability
For launch week I would reconcile source counts, ingestion counts, profile updates, journey entries, exits, sends and business outcomes. At the first divergence, the team should know which system owns the evidence and who investigates next.
Roll out in layers.
Identity + data contracts
Implement the event and profile contracts. No broad customer messaging yet. Validate schemas, ownership and profile behavior.
Logic without broad send
Run entry, exit and eligibility logic against synthetic and internal cohorts. Reconcile expected versus actual.
Small audience
Release one use case to a controlled audience with monitoring, support ownership and a control group where useful.
Expand proven patterns
Use real adoption, failure and conversion evidence to decide when the architecture and operating model are ready for more journeys or higher volume.
What changes at 10M users?
The conceptual architecture may stay recognizable, but the operating model has to mature:
- Formal event schema and data owner governance
- Automated contract tests between source services and ingestion
- Capacity and rate limit modeling for bursts
- More explicit privacy, retention and regional consent controls
- Monitoring for ingestion lag, stale data and count divergence
- Reusable lifecycle components instead of one-off journeys
- Change management rules for data consumed by multiple downstream use cases
- Operational runbooks, escalation paths and clear recovery ownership