Journeys
Build it once. It keeps running. A journey is an automation your AI CMO runs over time – a welcome series, a win-back play, a weekly content engine – built on a visual canvas and executed for you, person by person.
Creating a journey
Journeys in the sidebar gives you three ways in:
- Describe it. Type what you want – "every Monday, research AI news and draft a LinkedIn post" – and press Draft it. The AI builds the journey and opens it on the canvas for review. Nothing is saved until you save it.
- Templates. A gallery of ready-made journeys in two groups – Get customers (lead welcome, launch amplifier, SEO content engine, ad creative refresh and more) and Keep customers (win-back for at-risk, reactivation, loyalty boost, referral ask). One click opens the template on the canvas.
- Blank canvas. New journey starts you from a trigger and an empty canvas.
Every journey belongs to one brand profile and works in that brand's voice – the builder header shows which.
Or ask for one in Chat – see Journeys built in chat.
Journeys built in chat
Ask in Chat the way you'd brief a colleague – "a three-email win-back for customers who haven't bought in 90 days", "a welcome series when someone signs up". Chat builds the journey as a draft and opens it on the canvas beside the conversation, the same steps you see in Journeys. Nothing is live: a journey built in chat starts only when you press Start in Journeys.
The emails are written as it is built. Each email step is written while the journey is being built, saved to your Library under the journey's campaign, and shown on its step – a miniature, the subject and the pre-header. That email is what the step sends, word for word; each person still gets their own details in the fields it uses (their first name, their company). Click a step on the canvas to read its email at full size, at desktop or phone width.
Some emails are written each time the journey runs instead, and their step says Written when it runs, with the brief it is written from:
- an email that comes after a step producing something it uses – the code from a Grant promo step, research from Research topic – because it needs what that step produces for each run;
- every email in a Repeat journey, so each run sends something new.
Chat's reply says which emails were written and which wait for a run, and why.
Who it goes to. A journey for a group of people – a win-back, an offer to a segment – starts with an Audience trigger marked as needing setup. Choose the audience in Journeys before you press Start.
How the written emails go out follows your brand's own approval setting – Brand → Messaging → Review & approval, Require approval before a marketing send:
- On: each step holds every run until someone approves it – find them in Activity under Needs you.
- Off: the email goes out when a run reaches its step.
Service journeys – receipts, confirmations, account notices – are never held. Emails written each time the journey runs are always held for approval, whatever the setting: nobody has read them before they would go out.
Credits: building the journey costs 3 credits, and each email written for it costs what writing that email in chat costs (12 credits). An email written each time the journey runs is charged on every run.
The building blocks
Drag steps from the left rail. The main groups:
- Triggers – a journey can carry several, each with its own audience scope and its own on/off switch: Audience (a segment or picked people enter when you publish, or on a schedule – see An audience on a schedule), Manual (you run it yourself), Repeat (a schedule – hourly, daily, weekdays, weekly on one or several days, monthly), On event (a person enters when something happens – a form submitted on a specific landing page, a specific tracked link clicked, a lifecycle stage change, or one of your own events).
- Messaging: Email, SMS, Push, WhatsApp, Website message – covered below.
- Flow: Wait (pause for a while), Wait for event (hold the person until they do something), Wait until time (hold them until a date and time), Condition (branch yes/no – on rules built like an audience, on one event, on a profile attribute, "did they open the email?", "did they click a tracked link?" and more), Did they…?, In audience?, Day & time (weekends one way, weekdays the other), A/B split, Notify me (an alert to you with the run's results), and Goal (the finish line). Note and Section annotate the canvas for whoever opens it next – they are never executed.
- And the rest of the toolkit: social posts (LinkedIn, X and X threads, Facebook, Instagram), blog and SEO articles, images, ad drafts, web research, page scraping, tracked links, webhooks to call your own systems, and reusing content from your library. Content steps work draft-first – the AI writes, you approve.
Event triggers
The On event trigger enrolls a person the moment something happens. The list is your brand's own: every event your site, your systems or the platform sends for the brand appears in it, with how many have arrived so far and the stored name underneath (so it can be matched against your tracking plan). Everyone who can open the brand sees the same list.
- Activity – someone submits a landing-page form, clicks a tracked link, completes a survey, or is active on your website (daily)
- Website – someone is identified on your site (logs in or signs up), submits a form on your site, or converts – once per conversion type your site has sent, so someone signs up is its own line
- Website behaviour – someone views a page, scrolls down a page (or 25 / 50 / 75 / 90 / 100 % of it), leaves a page with how far they scrolled, clicks or sees a button, sees a page element, shows exit intent, rage-clicks, starts filling a form, or abandons one. These arrive about ten minutes after the visit, and only for visitors who were identified at the time (which needs their marketing consent)
- Lifecycle stage changes – becomes a New customer, a Potential loyalist, Loyal, a Champion, At Risk, Can't Lose, goes Hibernating or becomes Lost. A save play can run itself the day someone starts slipping
- Support – contacts support, has their issue resolved, turns unhappy in a conversation, or rates one
- Website messages – sees or clicks one of your website messages
- Everything else your brand holds – the e-mails you sent and what happened to them, purchases, and any event your systems push or your site sends (registered in Data → Events), under its own name
The event is chosen from the same searchable list the audience builder uses, with the same names. + Refine narrows it on any field the event carries – the channel a signup came through, the form, the page, how far someone scrolled, and the event's own amount, currency, channel, source and campaign: where channel is any of affiliate, paid_social, where amount is more than 100. The field list, the comparisons and the value suggestions are the audience builder's (see Building an audience). A filter row with a field but no value is marked, and the journey will not publish until it is finished or removed – an unfinished filter would otherwise let every event of that kind through.
Event triggers are polled continuously; a matching person is enrolled and their run starts within about a minute of the event reaching us. An event that reaches us late (a batch from your systems, the daily website activity) still starts the journey, as long as it happened in the last 48 hours – an import of older history never does.
The same list and the same filters are offered by Wait for event ("until this person …" – only an event that passes the filters wakes the person), by the Goal ("count a conversion when the person …"), and by the If / else check did they do something? (one event) (the Did they…? step in the palette): yes when the person viewed the bonus page at least once since they entered this journey, or in the last 7 days, narrowed by the event's fields. So a journey that starts on a login can branch on whether the person also viewed a page, deposited or abandoned a form. Website behaviour arrives about ten minutes after the visit – put a Wait before the check when it should see what they just did.
The If / else a profile attribute check reads any field of the person's contact record or their customer record – the same list the audience builder offers, with the same comparisons. It works however the person entered the journey.
If / else on rules
When one event or one attribute is not enough, the If / else check do they match rules? (events, profile, audiences) takes a whole rule, built in the audience builder: signed up since they entered this journey, and industry is agency or ecommerce, except people who approved a campaign in the last 30 days. Everything the builder offers works here – groups, any event with its filters, counts, sequences, totals of a value, profile fields, membership of an audience – plus one window of its own: since they entered this journey.
The panel shows the rule as lettered lines with their sizes, the rule in words, and Match right now – how many people would take the Yes path if they reached the step at this moment (a since they entered this journey window counts everything they did, because nobody has entered yet). Edit rules opens the full builder; Use these rules brings them back to the step. A did they do something? or a profile attribute check can grow into a rule with Combine with more conditions – the check becomes condition A.
The rule is checked for each person when they reach the step: Yes if they match, No if they do not or we cannot tell who they are. In Activity, a run shows which lettered conditions held. A journey with an unfinished rule will not publish.
Day & time – weekends one way, weekdays the other
The Day & time step (check what day and time is it?) sends people down one of two paths depending on when they reach it.
- On these days – pick the days, Monday to Sunday. Saturday and Sunday are picked to start.
- At these times – All day, or Between two times. A range that crosses midnight belongs to the day it starts on: Friday 22:00 to 02:00 includes 01:00 on Saturday.
- Whose clock – Each person's own time reads the time in the person's timezone when we know it, otherwise in the journey's send-hours timezone, otherwise UTC. A run with no person is read the same way without the person's own timezone.
- Name the paths – what the two ways out are called on the canvas (When it matches and Any other time). The names follow the days you pick – Saturday and Sunday read Weekend / Weekdays – until you type your own.
The step is checked the moment a person reaches it: put it right after the trigger to split on when they entered, or after a Wait to split on when they get there.
An audience on a schedule
An Audience trigger has When they enter: Once, when published (everyone in the audience enters when you publish, and publishing again picks up people added since) or On a schedule. On a schedule uses the same schedule settings as Repeat – every few hours, every day at one or several times, weekdays, chosen days of the week, or a day of the month. Each time it runs, the audience is refreshed and the people in it at that moment enter – blacklisted people never do, and every message they get is checked as usual (see Who enters – and who never will). Nobody enters at publish; the first entry is the first scheduled time, and Run now does one run the same way.
People still in the journey are never entered twice. Whether people who finished come back at the next run is the journey's re-entry setting in Settings – the trigger panel says which way it is set. Turn re-entry on when the same people should go through again.
Put together with Day & time:
- Audience – VIP players, on a schedule, every day at 10:00
- Day & time – Saturday and Sunday
- Weekend → the weekend offer e-mail
- Weekdays → the weekday reminder text
Starting a journey from your own systems (API)
Your backend can start a published journey for one person directly – "order shipped", "deposit made", "support ticket resolved" – without waiting for any poll:
POST https://theaicmo.com/api/journeys/trigger
Authorization: Bearer <your API key>
{
"journey_id": "<the journey's id>",
"email": "customer@example.com",
"event": { "order_number": "1042", "amount": 89.90 },
"idempotency_key": "order-1042-shipped"
}
- Create the key in Settings → API keys and tick Trigger
journeys. The journey's own brand is used, so you do not need to
send
client_id. A key can start any journey on a brand its owner may edit – including journeys a teammate built. - Name the person by
email,external_idor both. Someone who is not a contact yet is created on the journey's brand. When you send both, they must be the same person – if they point at two different contacts, nothing is enrolled and the answer is409 identity_conflict. - Someone the call creates starts with no e-mail marketing consent
("Not granted"): a Service journey reaches them, a Marketing
journey sends them no e-mail. When your own system holds their consent,
send
"consent": { "email": true }and it is recorded on their consent history. For someone already in your contacts it counts only when this "Not granted" is all that stops marketing to them – it never overrides an unsubscribe or a withdrawn consent – and leaving it out, or sendingfalse, never changes anyone. eventproperties (up to 20 – text, numbers or true/false) reach your message copy as{{ event.order_number }}, which fills with1042. Text longer than 500 characters is cut.idempotency_keymakes retries safe: the same key returns the same run instead of starting a second one – even when two retries arrive at the same moment. The key names the run, so use a new one for each person. A person already inside the journey is never double-enrolled either.- Every door still applies: people on a blocking suppression list are refused (and the refusal is recorded), unsubscribed contacts can enter service journeys but not marketing ones, and the journey's own re-entry rule decides whether someone can go through twice. A person erased on request is never re-created; if that cannot be checked at the moment, nothing is enrolled and the answer says to retry.
- The run starts within about a minute; the reply carries its id so you can find it in the journey's Activity.
Personal tokens – any message, their data
Every message step (email, SMS, push, website message) fills in
per-recipient tokens at send time. Write {{p.first_name}} and each
person gets their own name; any contact field works – {{p.company}},
{{p.phone}}, {{p.external_id}} – and so does any column you've ever
imported: a CSV with a loyalty_tier column makes {{p.loyalty_tier}}
available in every message with no setup.
The warehouse adds what no contact field holds – each person's actual
behavior, computed fresh at send time: {{p.total_spent}},
{{p.order_count}}, {{p.last_purchase_date}} (as "12 Jul 2026"), and
{{p.days_since_last_purchase}}. "You've placed 14 orders with us –
the last on 12 Jul 2026" writes itself.
Properties on your events work the same way – anything you see in the
Data hub. If your site sends a deposit event with method: "card",
then {{p.method}} fills with that person's most recent value. The
lookup only runs for tokens nothing else answered, so it costs nothing
on ordinary sends.
Add a fallback with a pipe – {{p.first_name|there}} writes "there"
when the field is empty, so "Hi ," never reaches a customer. A token
that doesn't exist renders as nothing.
Your brand's own details fill in the same way, from Settings → Brand:
{{brand.name}}, {{brand.website}}, {{brand.logo_url}} and
{{brand.primary_color}} – in an e-mail's subject and body, an SMS, a push,
a website message's headline, text and button, and a webhook body
(see Personalizing your messages).
On top of the field tokens you can define audience-valued tokens
under Audience → Personalization → Messages: one token name, a
different value per audience – {{p.offer}} can be "20% off" for VIPs
and "10% off" for everyone else, with an Everyone value as the
fallback. If a defined token shares a name with a contact field, the
defined token wins. The Messages tab lists every token your data
already supports, click-to-copy.
The Email step
The Email step is AI-written: you give it a prompt ("Monthly product update – friendly, one CTA to the changelog"), choose Campaign (one message, one CTA) or Newsletter (a multi-section issue), and the journey drafts it for your approval before anything sends. An email from your Library can be used as it is instead, and a journey built in chat arrives with its emails already written – see Journeys built in chat.
On the canvas each Email step shows what it sends: an email from your Library as a miniature with its subject and pre-header, an AI-written one as the brief it is written from. The pre-header is the line inboxes show after the subject; it goes out at the top of the email, personalised like the subject, and an email whose design already carries one keeps its own.
- Send to: by default, the person moving through the journey. You can instead broadcast to a segment – each run emails the whole audience, however large it is. It goes out in batches of 500 and picks up where it left off on the next run, so nobody is silently left out. Unsubscribed people are always skipped.
- Test send: send just that email to any address, subject prefixed [Test].
- Setup required once: email sending is configured per brand profile in settings – either The AI CMO sends for you on a verified domain, or use your own provider (Resend, Postmark, SendGrid, or any SMTP). Sending stays off until a verification email arrives, so a misconfigured domain can never send to customers. A domain can live on one Resend account only: if the managed setup says your domain is already registered on another Resend account – usually your own – choose use your own provider and connect with your Resend key instead.
The SMS step
SMS defaults to word-for-word: with "I write it" selected, the text you type is the message that sends – no rewriting, which is what most teams want for compliance-sensitive texts. Switch the Content selector to "the AI writes it from my brief" and the step writes the message fresh from your brief each run instead. Texts written in SMS Studio are in your library – choose from my library on the step ("or write it in SMS Studio" opens the studio). It needs SMS sending connected first (Twilio, Telnyx, Vonage, MessageBird, sms.to, or ProfiSMS), contacts need a phone number, and opted-out numbers are always skipped. STOP replies opt people out automatically.
Under the text, the step counts it the way it goes out – filled in for a sample person, never the placeholders themselves – for a text you write and a text from your library alike. For a text you write, the AI writes or that comes from the step before, Accents chooses between sending without accents (160 characters a part) and keeping them (one accented letter makes the text Unicode, 70 a part); each option shows its parts. Left on the brand's default, a Czech or Slovak brand sends without accents and every other brand as written. A text from your library carries its own choice, set on the text in SMS Studio: the step shows it with a link to change it there, so one text never has two settings. A library text passed on by a From-library step keeps its choice too. A test from the step is filled in for the sample person and sent with the same choice. Like any change to a step, a new choice applies to a live journey once you publish the changes.
Connecting a provider takes its credentials and a sender. For ProfiSMS the sender ID is optional – leave it blank and texts go out from your account's default – and when set it must be either a phone number in international form or up to 11 letters, digits, dots or dashes, registered with ProfiSMS; anything else is refused when you save rather than quietly altered on the way out. Verification checks the login against your account without sending anything, or sends one real test text if you give it a number. A postpaid ProfiSMS account verifies at any balance; a prepaid one needs credit.
The Push step
Sends a push notification to the person in the journey. Three routes, any of which works alone:
- Your mobile app, directly – ship
@aicmo/react-nativeand upload your Firebase service account on the Website SDK page; notifications go straight through your own Firebase project, no third party. - Your website – visitors who enabled notifications through the Website SDK get the push in their browser (real phone notifications on Android, iPhone once your site is on their home screen).
- OneSignal – if your app already runs OneSignal, connect it (Connections → search "OneSignal") and the step notifies through it, matched by the customer id your app registers.
Like SMS, the message defaults to word-for-word ("I write it"), with an AI mode that writes from your brief. You set the title, where a tap lands, and which routes to use – every route available, mobile app only, or browsers only – and the panel previews the notification as the phone will show it. People on your blacklist or self-exclusion lists are never sent – the block happens before anything leaves, on every route.
The WhatsApp step
Messages the person on WhatsApp through your own WhatsApp Business account (Meta Cloud API). Connect it once under Connections → WhatsApp sending: phone number ID, WhatsApp Business Account ID, and a permanent access token – all three from Meta Business Suite → WhatsApp → API Setup. We verify against Meta before saving.
One honest rule, and it's Meta's, not ours: marketing messages must use pre-approved templates. The node lists your account's approved templates; you pick one, fill its {{1}}, {{2}} variables, and the panel previews the message as the chat will show it. Create and manage templates in WhatsApp Manager – once Meta approves them, they appear in the picker.
Send to the person in the run or broadcast to an audience (contacts need phone numbers; a large audience goes out in batches of 500 and continues on the next run until everyone has been reached). Opt-outs are shared with SMS – a STOP on either channel stops both – and blacklist / self-exclusion rules apply before anything is sent. An optional weekly frequency cap works like the SMS one.
The Website message step
Shows a message on your own site to the person in the journey – a corner card, top banner, or modal, rendered by the Website SDK. You control when it appears (immediately, after a delay, at scroll depth, on exit intent, when they arrive from this journey's email, or after an event), how long it stays live, and whether a dismissal is final.
Each website message can carry a measured goal – "success is a purchase / a conversion / a form submitted within N days of seeing it" – counted against your warehouse, with revenue summed. The node shows its performance right on the canvas: shown, clicked, converted, and revenue.
Who enters – and who never will
- Audience triggers enroll everyone in the segment when you publish; publishing again picks up people added since. Each person goes through a journey once.
- Enroll people in the Activity panel lets you hand-pick contacts into a running journey.
- Blacklisted people are dropped at the door – they never enroll, and the block is logged. Unsubscribed and opted-out people are skipped at every send step. Frequency caps skip anyone already at their limit – the weekly cap in your sending settings (at most N marketing messages per person per 7 days, across all journeys), and a cross-channel budget on the brand profile if you set one. Service messages are never capped.
Marketing or Service
Under Settings, a journey is either Marketing or Service.
Marketing is the default and everything above applies to it.
Service is for messages the customer needs rather than wants – order confirmations, password resets, payment and account notices. A service journey reaches people who unsubscribed from marketing, so it works differently on purpose:
- People can re-enter every time it fires. A customer's second order needs its confirmation as much as their first.
- Send hours and the frequency cap don't apply, and no unsubscribe link is added.
- No control group is held back, because a "control" for a receipt is a customer who never got one.
- It can only start from an event or be run by hand, and each step reaches the one person the event is about. Audience triggers, repeat schedules and sending a step to a whole segment are all refused when you publish.
Hard blocks still stop it: bounced addresses, spam complaints, and anyone on a blacklist set to "Blocks everything". Service email also needs its own verified sending domain – see Unsubscribes, opt-outs, and the blacklist.
Running and watching
- Save keeps a draft. Start publishes; once live, edits go out with Publish changes.
- Test rehearses the published version without publishing anything. Run now publishes what's on the canvas and runs it.
- The live canvas shows the running step pulsing, with per-step counts of who's here and who has passed.
- The Activity panel is the full ledger – every run, step by step: drafts waiting for your approval, sends, branches taken, skips with their reasons, and completions. Filter by Needs you to see only the runs waiting on your approval.
Delivery status
Under every email and SMS step of a run, the Activity panel shows what the provider reported after the send:
- Delivered with the time, once the provider confirms it. For SMS this is the carrier's delivery receipt; for email it is the provider's delivered event. An email that was opened or clicked says so too.
- Bounced (email) or Not delivered (SMS) with the provider's reason – an expired message, a wrong number, a dead mailbox. A bounced address is also put on the suppression list, so it is not tried again.
- Delayed when the mailbox rejected the message temporarily and the provider keeps trying.
- Sent – no delivery receipt yet right after the send, until the provider reports. Most receipts arrive within a minute; some carriers take hours.
The same lines appear on the person's timeline under Customers, so you can answer "did they get it?" for one person without opening the journey. Receipts need the provider's webhook set up under Connect: ProfiSMS receipts are requested with every message we send and need no setup – and every few minutes we also ask ProfiSMS about any SMS still without a verdict, so a lost receipt (or a message sent before receipts were switched on) still gets its status. Twilio, Resend, Postmark and SendGrid need the webhook URL from the sending card pasted into the provider. Email sent through Resend – your own account or our managed sending – is also asked about every few minutes while it has no verdict, so a status arrives even without the webhook.
Versions – every publish is kept
Every time you publish, the journey is frozen as a numbered version, and runs already in flight keep following the version they started on – your edits never disturb them. Click the Live · v3 (or Paused) chip in the builder header to see the history. Open any version to put it on the canvas; if it's the one you want back, Save or Publish it – that makes it the new current version, so the history stays an honest record of what actually ran, and when.
Moving a journey between brands
Build in a test brand, then move it to production – or hand a proven journey to a second brand:
- Copy to another brand (Settings → Copy & export) duplicates the journey into any other business profile on your account. The copy lands as an unpublished draft – nothing runs until you publish it there.
- Export as a file downloads the journey; Import (top of the Journeys page) brings it back in – into another brand, or another account entirely.
Either way, the steps, prompts, schedules, send hours and layout all travel. Brand-specific picks do not: audiences, hand-picked people, promos, tracked links and surveys belong to the brand they were chosen in, so those choices are cleared and listed for you – the copy shows a "needs setup" badge exactly where a new pick is needed. Re-select them in the target brand before publishing.
Measuring what a journey earns
Turn on the Uplift holdout and a random slice of enrollments (5, 10, or 20%) is deliberately left unmessaged as a control group. Comparing their revenue against everyone else's tells you what the journey actually caused – not just what happened near it.