All playbooks
Playbook 5 of 6

Sync your audience

Campaigns and drip sequences target segments, and segments are built from customer attributes. Keep those attributes flowing from your app, and lifecycle messaging becomes configuration instead of code.

20 min You ship: Customer profiles that update themselves, plus your first segment and an active welcome drip.
1

Centralize identify

One function decides what your app knows about a customer. Everything else (login hooks, backfills, imports) calls that function, so the attribute set can never drift between call sites.

01 · identify pipeline
Wire customer profile sync into this app using the Messy client's identify method.

1. Create one central function (for example MessyIdentifier.attributes_for(user)) as the single source of truth for what we send. It returns {email, first_name, last_name, custom_attributes} where custom_attributes is a flat object of the fields we will want to segment on: plan, role, country, signed_up_at (ISO 8601), plus the product-specific facts that define lifecycle stages in this app. Analyze the domain model and propose them: things like has_completed_onboarding, project_count, last_order_at. Primitive values only; drop null entries.
2. Call it, through the background job wrapper, on signup, on login, and on any update to a field it reads. Skip users without an email; Messy keys customers on email.
3. Messy shallow-merges custom_attributes on every identify, so partial updates are fine, but keys must stay consistent: renaming one orphans the old key on every existing profile. Put a short comment saying exactly that above the attributes function.
4. Add a backfill task messy:identify that iterates all existing users in batches and identifies each one, avoiding N+1 queries on any derived attribute.
2

Create a segment and a welcome drip

You manage segments and drips over the same API, with the same key. This one runs as a one-off script, not app code, and asks for your go-ahead before anything activates.

02 · segment + welcome drip
Using the Messy API (same MESSY_API_URL / MESSY_API_KEY Bearer auth), create our first lifecycle automation: a welcome sequence for new signups. Do this as a one-off script or console session, not app code.

1. Check the available segment attributes: GET /segments/attributes. Custom attributes appear as custom.<key>.
2. Create a segment: POST /segments with {"name": "New signups", "conditions": {"operator": "and", "conditions": [{"attribute": "custom.has_completed_onboarding", "operator": "eq", "value": "false"}]}}. Adjust the condition to this product's definition of a fresh account. Preview membership first with POST /segments/preview {"conditions": ...}, which returns a count and a sample.
3. Find template ids: GET /templates, matching the welcome templates we synced (day 0 welcome, day 2 activation nudge, day 7 check-in). If they don't exist yet, add them to templates/ and run messy:sync first.
4. Create the drip: POST /drips with {"name": "Welcome sequence", "segment_id": <id>, "steps": [{"position": 1, "template_id": <id>, "channel": "email", "delay_days": 0}, {"position": 2, "template_id": <id>, "channel": "email", "delay_days": 2}, {"position": 3, "template_id": <id>, "channel": "email", "delay_days": 7}]}. Customers who leave the segment (they finished onboarding) exit the sequence by default, which is what we want.
5. Sanity-check reach with POST /drips/projection {"segment_id": ..., "steps": [...]} and show me the numbers before activating anything.
6. After my confirmation, activate with POST /drips/<id>/activate. Email steps must render an {{ unsubscribe_url }} link; ours is in the layout footer, so this passes.