← Back to blog

Flows: Tag a Contact, Let the Sequence Run Itself

No cron job, no scheduler table, no forgotten unsubscribe check. Tratto's automation engine reacts to your own events and runs the sequence for you.

Every product eventually needs "when X happens, email this person a few times over the next few weeks." A welcome series. A re-engagement nudge. A founding-offer countdown. And every team eventually builds the same fragile thing to make it happen: a cron job, a table tracking who's on step 3 of 5, and a prayer that nobody unsubscribes mid-sequence without the code noticing.

We built Flows so you don't have to build that table.

How It Works

A flow has a trigger and a list of steps. When the trigger fires for a contact, they're enrolled, and the steps run in order — send an email, wait, branch on a condition, update the contact, call a webhook, repeat.

The important part: a flow with a 14-day wait in the middle costs you nothing while it waits. There's no process sitting idle, no server to keep warm, no risk of losing state on a deploy. Enrollment and every step are independent, event-driven jobs. You configure the sequence once; the infrastructure keeps its promise to run it, on schedule, indefinitely.

curl -X POST https://api.tratto.email/v1/flows \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Welcome series"}'
curl -X PATCH https://api.tratto.email/v1/flows/flow_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "trigger": { "type": "contact_tag_added", "config": { "tagName": "onboarded" } },
    "steps": [
      { "id": "s0", "type": "send_email", "config": { "templateId": "tmpl_welcome", "subject": "Welcome aboard", "from": "[email protected]", "fromName": "Your Company" } },
      { "id": "s1", "type": "wait", "config": { "delay": "3", "unit": "days" } },
      { "id": "s2", "type": "send_email", "config": { "templateId": "tmpl_tips", "subject": "Three things worth trying", "from": "[email protected]", "fromName": "Your Company" } }
    ]
  }'

Then activate it — and activation is a real gate, not a flag flip. Every send_email step needs a sender on a domain you've already verified: if the domain isn't verified, activation fails and tells you to go add it first. The resolved sender is then frozen onto the flow, so a later change to your defaults can't silently repoint a running sequence.

The Trigger Is Whatever You Already Track

Tags are the trigger you'll reach for most often, because you almost certainly already have the signal somewhere in your own product: a user finished onboarding, a trial is about to expire, someone viewed pricing three times this week. Tag the contact when that happens, from your own code, and the flow takes it from there.

There are five triggers in total: contact_joins_audience, contact_tag_added, contact_tag_removed, email_event, and manual.

PATCH /v1/contacts/:id replaces the whole tags array — it does not append. Any tag you leave out of the array is removed from the contact, and every removal fires contact_tag_removed, which can enroll that contact in a different flow. Send the complete list, not just the new tag.

So read the contact first, then send the full array back with your new tag added:

# 1. Read the current tags
curl -s https://api.tratto.email/v1/contacts/cont_abc123 \
  -H "Authorization: Bearer tratto_live_..."
# → { "data": { "tags": ["beta", "eu"], ... } }
 
# 2. Send them all back, plus the new one
curl -X PATCH https://api.tratto.email/v1/contacts/cont_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"tags": ["beta", "eu", "onboarded"]}'

Get that wrong and you don't just lose the other tags — you fire a removal event for each one. Everything after the tag, though, is Tratto's problem: the timing, the retries, the "did they already unsubscribe" check.

We Run Our Own Waitlist on This

The signup flow on this very site is a Flow. The moment someone confirms their email, we tag them waitlist-confirmed-it or waitlist-confirmed-en depending on the language they signed up in, and a flow per language takes over: a welcome email immediately, then a short story about why we're building Tratto, a pricing breakdown, a product preview, and a founding-member offer, spread across the following weeks. Nobody on our team tracks who is due for which one.

It's the same primitive described above, running in production, today.

What a Step Can Do

  • send_email — send a template, with the contact's own fields (firstName, email, any custom field) automatically available as {{tokens}} in the copy
  • wait — pause for a set amount of time before continuing
  • branch — evaluate a condition on the contact and take one of two paths; if you don't set a false path, the flow simply ends there for that contact
  • update_contact — set a field, add a tag, remove a tag — useful for marking someone as "nurtured" so a later flow doesn't double-enroll them
  • webhook_call — notify your own backend the moment a contact reaches that point in the sequence

That last one turns a flow into more than an email tool: tag a contact high-intent from your product analytics, and a webhook step can ping your sales channel the second it happens. The flow becomes the trigger for a human process, not just another automated send.

A Few Ideas to Steal

Onboarding. Tag on signup, drip a few "have you tried X" emails over the first two weeks, each pointing at a different feature.

Win-back. Run your own inactivity check on a schedule, tag anyone who's gone quiet, and let a flow handle the nudge — one check-in, then an incentive, then a final "we'll stop emailing you" paired with an automatic unsubscribe if they still haven't come back.

Sales handoff. Tag high-intent behavior and use a webhook step to alert a human, in real time, without anyone watching a dashboard.

Lifecycle hygiene. Tag contacts entering a state — trial expired, payment failed — and use an update_contact step to keep a lifecycle_stage field consistent everywhere else you segment.

None of it needs a scheduler you maintain. Tag the contact, configure the sequence once, and let it run.


Flows are part of Tratto today. Read the full documentation for every trigger and step type, or join the waitlist if you're not on it yet.