Skip to main content

Flow recipes

Complete, working flows you can build in a few minutes each. Every recipe lists the blocks, their settings, and the reasoning behind them.

tip

Every recipe below can also be described to Hermes in a sentence — "qualify a new WhatsApp lead and send them to sales or support" — and built for you. The recipes are still worth reading: they show what a good flow looks like, which makes it easy to check the AI's work.

RecipeTriggerGood for
Instant welcome replyIncoming MessageAny new inbox
After-hours auto-replyIncoming MessageTeams with fixed hours
Qualify and route a new leadIncoming MessageSales and support split
Escalate an unhappy customerConversation ChangedRetention
Push a new lead into an external CRMForm filledIntegrations
Start a conversation from an external systemIncoming webhookOrder and delivery updates
Daily morning summaryScheduled TimeManagers
Alert a manager when a metric slipsAnalytics AlertOperations
Phone menu with agent transferIncoming CallVoice

1. Instant welcome reply

Answer within a second, so nobody waits in silence.

Incoming Message (Inboxes: WhatsApp)
└─ Reply Message
└─ End Session
BlockSettings
Incoming MessageTick your WhatsApp inbox.
Reply MessageHi {{contact.name}}! Thanks for contacting Acme. One of our team will be with you shortly. · Wait for client response: off
End SessionResolve the conversation: off

Why End Session? Without it, Studio keeps owning the conversation and it sits in the Studio Bot queue. End Session releases it so it appears in Waiting in line for an agent to pick up.

tip

{{contact.name}} is empty for a first-time contact on some channels. Either write a greeting that reads well without it, or branch on contact.name with the exists operator.


2. After-hours auto-reply

Say something useful when you are closed, and stay quiet when you are open.

Incoming Message
└─ Business Hours
├─ Open ──── (nothing — let agents handle it)
└─ Closed ── Reply Message ── End Session
BlockSettings
Incoming MessageLeave inboxes empty to cover every channel.
Business HoursPick the schedule you created in Settings → Business Hours.
Reply Message (Closed)Thanks for your message! Our team is offline right now. We're open Sunday–Thursday, 09:00–18:00, and will reply first thing.
End SessionResolve: off

Leaving the Open port unconnected is deliberate — during working hours the flow should do nothing at all and let agents reply normally.


3. Qualify and route a new lead

Ask one question, then send the conversation to the right team with the right label.

Incoming Message
└─ Reply Message (wait for reply)
└─ Condition If/Else
├─ Sales ───── Change Conversation (label: sales, hand over to queue)
├─ Support ─── Change Conversation (label: support, hand over to queue)
└─ Else ────── Reply Message "Sorry, I didn't catch that…"
BlockSettings
Reply MessageHi! Are you contacting us about **sales** or **support**? Just reply with one word. · Wait for client response: on · Wait timeout: 60
Condition If/ElseMatch mode: First match only · Add ELSE branch: on
— IF 1Label Sales · Value message.content · Operator contains · Compare as Text · Compare to sales
— IF 2Label Support · Value message.content · Operator contains · Compare as Text · Compare to support
Change Conversation (Sales)Labels mode: Add · Labels: sales · Human intervention: on · Hand over to: Waiting in line
Change Conversation (Support)Same, with the support label.
Reply Message (Else)Sorry, I didn't catch that. Please reply with "sales" or "support". · Wait for client response: on

Why contains and not equals? People type "sales please" and "I need SALES". contains is not case-sensitive and matches inside a sentence.

The ELSE loop. The ELSE branch asks again and waits again, so the customer gets another chance. Consider adding a Save as Variable counter, or a third strike that hands over to a human, so a confused customer is never stuck.


4. Escalate an unhappy customer

React the moment AI detects frustration.

Conversation Changed (AI Signals: Feelings / emotion detected)
└─ Condition If/Else
└─ Negative ── Change Conversation (priority: urgent, flag: important)
└─ Private Note
└─ Send Email
BlockSettings
Conversation ChangedAI Signals: Feelings / emotion detected. Leave the other filters on any.
Condition If/ElseIF Negative · Value conversation.customer_emotion · Operator less than · Compare as Number · Compare to 3
Change ConversationPriority: Urgent · Flag: Important · Human intervention: on · Hand over to: Waiting in line
Private Note⚠️ AI detected negative sentiment ({{conversation.customer_emotion}}). Signal: {{ai.message}}
Send EmailTo your team lead · Subject Escalation: {{contact.name}} · Body includes {{conversation.id}} and {{ai.message}}

5. Push a new lead into an external CRM

Send every form submission straight to another system, and record the result.

Form filled (Form: Contact us)
└─ HTTP Request (capture response: on)
└─ Condition If/Else
├─ Success ── Save as Variable ── Private Note
└─ Else ───── Send Email (alert the admin)
BlockSettings
Form filledPick the specific form.
HTTP RequestMethod POST · URL your CRM endpoint · Header Content-Type: application/json · Body type JSON · Capture response on · Timeout 15
Condition If/ElseIF Success · Value http.ok · Operator equals · Compare as Boolean · Compare to true
Save as VariableKey var.{{contact.id}}.crm_id · Value {{http.json.id}} · Type Text
Private NoteLead synced to CRM as {{http.json.id}}.
Send Email (Else)To your admin · Body includes {{http.status_code}} and {{http.error}}

JSON body:

{
"name": "{{answers.full_name}}",
"email": "{{answers.email}}",
"phone": "{{answers.phone}}",
"source": "{{sourceParams.utm_source}}",
"answers": {{answers.list}},
"teloring_contact_id": "{{contact.id}}",
"teloring_execution_id": "{{system.execution_id}}"
}

Note the unquoted {{answers.list}}. Because the variable fills a whole value, Studio inserts the real array instead of a string. See Variables in a JSON body.


6. Start a conversation from an external system

Let your order platform tell a customer their delivery is on the way.

Incoming webhook
└─ Reply Message
└─ Contact Update
BlockSettings
Incoming webhookCopy the URL · Allowed methods POST · Learn next request on, then send one real request.
Reply MessageConversation ID {{webhook.body.conversation_id}} · Message Good news {{contact.name}} — order {{webhook.body.order_id}} is out for delivery. Track it here: {{webhook.body.tracking_url}}
Contact UpdateField last_order_status{{webhook.body.status}}

Getting the conversation ID. A webhook flow has no conversation of its own, so {{conversation.id}} is empty. Either have the caller send a conversation ID in the payload, as above, or look it up with an HTTP Request first.


7. Daily morning summary

A private note in a chosen conversation, or an email, every working morning.

Scheduled Time (Sun–Thu, 08:30, Asia/Jerusalem)
└─ HTTP Request (capture response: on)
└─ Send Email
BlockSettings
Scheduled TimeFive rows: Sunday–Thursday, hour 8, minute 30, timezone Asia/Jerusalem.
HTTP RequestFetch whatever numbers you want to report. Capture response on.
Send EmailTo the team · Subject Morning summary — {{datetime.result}} · Body built from {{http.json...}} values

Add a Date and Time block before the email (operation Format date, format %d/%m/%Y) if you want a friendly date in the subject.


8. Alert a manager when a metric slips

Wire an Analytics alert to a real notification, and let people know when it recovers.

Analytics Alert (Alerts: "Waiting conversations over 20")
├─ Alert ────── Send Email ── Change Conversation (priority: urgent)
└─ Recovered ── Send Email
BlockSettings
Analytics AlertTick the alert you created in Analytics.
Send Email (Alert)Subject 🔴 {{alert.name}} · Body {{alert.report_title}} is {{alert.current_value}} ({{alert.direction}} the threshold of {{alert.threshold}}) as of {{alert.crossed_at}}.
Send Email (Recovered)Subject 🟢 {{alert.name}} recovered · Body {{alert.report_title}} is back to {{alert.current_value}}.

The alert fires once per crossing, so you get one email — not one per minute while the number stays high.


9. Phone menu with agent transfer

A complete inbound call experience.

Incoming Call (record: on)
└─ Play Sound "Welcome to Acme. This call may be recorded."
└─ Business Hours
├─ Open ──── IVR Menu
│ ├─ 1 · Sales ──── Agent Availability
│ │ ├─ Online ── Forward to Agent ──┬─ Completed ─ HTTP Request
│ │ │ └─ Rejected ── Play Sound ─ Hang Up
│ │ └─ Offline ─ Play Sound ─ Hang Up
│ ├─ 2 · Support ── Forward to External Phone
│ ├─ Timeout ────── Play Sound ─ Hang Up
│ └─ Invalid ────── Play Sound ─ Hang Up
└─ Closed ── Play Sound ─ Hang Up
BlockSettings
Incoming CallVoice inbox: your number · Record this call: on
Play Sound (greeting)WAV that includes the recording announcement.
Business HoursYour schedule.
IVR MenuMenu prompt WAV · Digits 1 · Sales, 2 · Support · Input timeout 7000 · Max attempts 2
Agent AvailabilityCheck Specific agents → your sales team · Consider online when At least one
Forward to AgentRouting Any available agent · Ring timeout 30
HTTP Request (Completed)Log {{call.duration_seconds}}, {{call.talk_duration_seconds}}, {{call.agent_name}} to your system.
Forward to External PhoneYour support line, in international form. Bills credits.
Hang UpCause Normal clearing

See Voice call flows for the full block reference.


Patterns worth reusing

PatternHow
Always give people an exitEvery waiting flow needs an ELSE branch or a handover, so a customer is never trapped.
Label before you hand overA conversation that arrives in the queue already labelled and prioritized saves the agent a step.
Leave a note, not a mysteryA Private Note explaining what the bot collected is worth more to an agent than a clean timeline.
Log the IDsSend {{conversation.id}}, {{contact.id}}, and {{system.execution_id}} to any external system you call.
Sticky-note your branchesSix months later, a sticky note explaining why a branch exists is the difference between improving a flow and rebuilding it.
One question per messageTwo questions get one answer, and your condition has nothing clean to match.

Next