Loading tutorials…
Loading tutorials…
When a Customer.io workflow isn't firing, 80% of the time it's an event-delivery problem upstream, not the workflow itself. This is the diagnostic path that finds the root cause in minutes instead of days.
Who this is forSaaS teams whose Customer.io workflows are firing inconsistently, or whose event volumes don't match their product analytics tool. If you've ever said 'why didn't user X get the email?' more than twice, this tutorial is for you.
What you'll need
Step 1
Start at the source — your backend. Does the identify/track call execute at all? Add a debug log, reproduce the incident.
Find the code path that should fire the event (e.g., the function that runs on subscription started).
Add a debug log immediately before the Customer.io SDK call: `console.log('[CIO] about to fire Subscription Started for user:', userId)`.
Reproduce the incident (e.g., have a test user start a subscription). Check application logs for the debug line.
If the log line doesn't appear, the code never executed. Look upstream — feature flags, conditionals, transactional rollbacks, async timing issues. The event wasn't suppressed by Customer.io, it was never sent.
If the log line DOES appear but no event in Customer.io, move to the next step — API delivery.
Step 2
Settings → Activity → Logs. Shows every API call hitting Customer.io with status, payload, and errors.
Customer.io → Settings → Activity Log (or Logs depending on UI version).
Filter by time window around the incident. Search for the user_id you tested with.
Look for the track or identify call. Status should be 200 (success). 4xx = client error (your fault, payload issue). 5xx = Customer.io side issue (rare).
If status is 4xx, click the request to see the error detail. Common 4xx causes: invalid identifier, missing required field, malformed JSON, expired API key.
If no log entry at all for your user_id around the timestamp, the API call never reached Customer.io. Check your backend HTTP client for errors, timeouts, or rate limits.
Step 3
Customer.io → People → search by user_id. Profile should exist. If not, identify call never landed.
Customer.io → People → search by user_id (or email — whichever is your primary identifier).
If no profile exists: identify call never reached Customer.io. Trace back to your backend identify firing — usually the issue is identify fires async after track and arrives in wrong order, OR identify firing in development not production.
If profile exists but missing attributes you expected: identify payload was incomplete. Inspect the actual identify call payload — Customer.io UI shows last received identify on the profile.
If profile exists but events not on timeline: track call has wrong identifier. Common cause: track call uses `email` while identify used `user_id`. Customer.io creates an orphan profile for the email and the real profile sees no event. Match the identifier across all calls.
For SaaS with custom identifier merging (anonymous to identified via cio_id): verify the merge happened. People → click profile → Identifiers section shows all merged identifiers.
Step 4
Event delivered correctly but workflow didn't fire? Inspect the workflow's trigger and filters.
Open the workflow in Journeys → click → Trigger settings.
Verify trigger exactly matches the event you fired. Event name must match EXACTLY including casing ('Subscription Started' ≠ 'subscription_started').
Check filter conditions: workflow may filter to specific segments (e.g., paying users only). If your test user doesn't meet the filter, they're excluded silently.
Check workflow state: workflow must be Live, not Draft. Draft workflows ignore events.
Check workflow frequency cap: if user already entered this workflow recently, they may be blocked by 'once per user' or 'once per N days' caps.
For segment-entry triggered workflows: verify the user is actually in the trigger segment. Segments → click segment → search for user.
Step 5
Workflow triggered but user didn't get the email? Trace step-by-step through the workflow.
Open the workflow → Activity tab.
Search for the user_id. Their position in the workflow shows: which step they're on, which steps they passed, which they exited on.
If they exited early: check exit conditions (goal hit, segment exit, opt-out).
If they paused at a Time Delay: that's expected. Wait or skip ahead.
If they hit a conditional branch and went the unexpected direction: inspect the branch logic. Most common cause: branch uses an attribute that wasn't set on the profile at trigger time.
If they reached the Email step but no email sent: check email-level filters (sometimes redundant with workflow filters). Check if user is in suppression list. Check sender domain reputation — Customer.io may suppress sending if domain is in cooldown.
Step 6
Email/SMS/push attempted but never received? Check delivery status, bounces, suppression.
Customer.io → People → click profile → Activity timeline.
For email: see Email Sent → Email Delivered → Email Opened sequence. If you see Sent but no Delivered, it bounced — check the bounce reason. Hard bounces (invalid email) get permanently suppressed.
For email landing in Spam: check Gmail Postmaster Tools for sender reputation. Check DMARC report for authentication failures.
For SMS: check Twilio dashboard for the message status. Carrier-filtered messages show as Delivered in Customer.io but never reach the phone — Twilio Console shows the carrier rejection.
For push: check device token registration. People → profile → Devices section. If no device token, push will silently skip. User may have uninstalled the app or revoked notification permissions.
Step 7
After fixing, instrument so the next occurrence is caught in minutes, not days.
Customer.io → Settings → Webhooks → + Create webhook subscription to relevant events: email_bounced, email_dropped, sms_failed, push_failed.
Forward webhook to your monitoring system (Datadog, Sentry, PagerDuty, or a simple Slack channel).
Set up a daily reconciliation: count events fired from your backend in last 24h vs received in Customer.io. Alert if divergence >5%.
For critical workflows: add a heartbeat email to yourself daily ("Did the activation workflow fire X times today?"). If it stops, you know within 24 hours instead of 4 weeks.
Document this incident in a runbook: what symptoms, what was the cause, what was the fix, how was it diagnosed. Future incidents reference the runbook.
Common mistakes
Identifier mismatch between identify and track calls
What goes wrong: Track call uses email, identify call uses user_id. Customer.io creates two profiles per user. Workflow attribution splits. Activation campaigns target users at 50% of intended rate.
How to avoid: Audit all identify and track call sites. Ensure ALL use the same identifier (user_id is recommended). Server-side wrapper enforces this at compile time.
Event name typo creates a "ghost" event Customer.io tracks but no workflow listens to
What goes wrong: `Account Crated` (typo for `Account Created`) fires from production. Activation workflow listens to `Account Created` — never triggers for these users. Customer.io UI shows the typo'd event with usage, but it's orphan data.
How to avoid: Activity → Events tab → audit all event names. Rename typos using event aliasing. Add the typo as an alias to the correct name. Fix the code in next deploy.
Workflow filter excludes users who should match
What goes wrong: Workflow targets `is_paying == true`. User just upgraded but their `is_paying` attribute hasn't synced yet (sync delay). They miss the post-upgrade workflow entirely.
How to avoid: Filter on events ("Subscription Started in last 1 day") rather than profile attributes when attribute sync delay matters. Or fire identify with `is_paying: true` BEFORE firing the track event that should match.
Backend API rate limits dropping events silently
What goes wrong: Burst of events (e.g., during a big product launch) exceeds Customer.io's per-second API rate limit. Events return 429 and your backend doesn't retry. 5-15% of events dropped during the spike.
How to avoid: Implement retry-with-backoff on the Customer.io SDK calls. Queue events in your backend (Redis, SQS) and consume at a steady rate. Most Customer.io SDKs have built-in batching — use it.
Test events polluting production workspace
What goes wrong: QA fires test events at production workspace. Workflows trigger for fake users. Real users miss campaigns because of conversion-exit confusion. Reports get noise.
How to avoid: Use Staging workspace for ALL test traffic. If a single workspace setup, prefix test events with `__test_` and filter them out at workflow level. But really — set up Staging workspace.
Timestamp ignored, event arrival time used
What goes wrong: Backfilling 30 days of historical events. Customer.io treats them as 'just happened' because timestamp wasn't passed. Workflow filters like 'event happened in last 7 days' match every historical event, triggering thousands of campaigns to users.
How to avoid: When backfilling, pass explicit `timestamp` on every track call. Run backfills in a controlled batch (rate-limited) with workflows paused. Resume workflows after backfill is done and verified.
Recap
Done — what's next
How to instrument Customer.io event tracking for a SaaS product
Read the next tutorial
Hand it off
Diagnostic skill compounds with experience. A specialist who's debugged 100+ Customer.io incidents recognizes patterns in minutes instead of hours. Typical engagement: $400-800 for a one-time audit of your workspace + a runbook for your team at $14-16/hr. Ongoing on-call support usually $300-800/mo.
See specialist rates
Three usual causes: (1) Customer.io receives server-side events only, while Mixpanel/Amplitude often have additional client-side capture. (2) Customer.io rate limits drop events during spikes if you don't batch. (3) Customer.io deduplicates events with same idempotency key while Mixpanel may not. Run a 1-day reconciliation per event name to identify which cause is yours.
Healthy: under 10 seconds. Acceptable: under 60 seconds. If lag is consistently >2 minutes, you have a queuing or batching issue on your backend — Customer.io processes ingest within seconds at scale.
If you log them on your side with timestamps, yes — replay them as track calls with explicit timestamps. Customer.io accepts events with historical timestamps up to ~13 months back. After that, the timestamp gets normalized to 13 months ago for ingestion.
Check: (1) Did the workflow get edited recently? Edits can break triggers silently. (2) Did the upstream event schema change? Property removed/renamed breaks filter logic. (3) Did the workflow hit a frequency cap quietly? Some users may be excluded from re-entering. (4) Is the workflow status accidentally Paused? Compare current behavior to git history of your event schema + Customer.io's change log.
Use Staging workspace with test users + test email addresses you control. Customer.io's 'Preview' feature renders templates without sending. For broadcasts, send to a tiny segment of internal teammates before sending to scale audience.
Check (in order): (1) Profile has correct email (not test@ or example@). (2) Email isn't in suppression list. (3) Sending domain DKIM/SPF green. (4) Email not bouncing — check bounce reason. (5) Email landing in spam — check Gmail Postmaster. (6) Recipient mailbox full — rare but happens. Most 'no email' issues are #2 or #4.
Customer.io
Customer.io is only as smart as the events you send it. Sloppy instrumentation — events fired client-side, inconsistent naming, missing properties — silently sabotages every workflow you build on top. This is the schema that scales.
Customer.io
Segments are how Customer.io decides who. Workflows are how it decides when and what. Most SaaS teams get one or the other right but rarely both — and the gap shows up as activation campaigns missing 40% of eligible users.
Customer.io
Customer.io Data Pipelines is the CDP layer that lets you instrument once and fan out to many tools. For SaaS teams paying Segment $1K-10K/mo, this typically consolidates the spend while keeping the same instrumentation pattern.
Customer.io
Customer.io's email side has three distinct surfaces — Broadcasts, Newsletters, and Transactional — and each has its own rules. Mix them up and you'll send marketing content from your transactional IP (bad) or transactional from your marketing IP (worse).
Customer.io
Liquid is what turns Customer.io from 'mass blast tool' into 'one-to-one personalization at scale.' Knowing the right 20% of Liquid syntax — variables, defaults, conditionals, filters — covers 95% of SaaS personalization use cases.
Customer.io
DIY Customer.io is the right call — until it isn't. In healthy SaaS, lifecycle email + in-app should drive 20-35% of activation and 10-20% of retention. If yours is at 5-10%, the gap is the program isn't being worked. Here's the honest framework for when to hire.