Examples
Each example shows the full flow definition JSON plus the API call to start a run. Adapt the IDs and channel names to match your tenant.
1. Onboarding sequence
Send a welcome WhatsApp message, wait 1 day, send a tutorial link, branch on whether the link was clicked.
{
"schema_version": 1,
"trigger": { "kind": "manual", "event_name": null },
"entry_node_id": "welcome",
"nodes": [
{
"id": "welcome",
"type": "send",
"config": {
"channel_chain": ["whatsapp"],
"channel_content": {
"whatsapp": {
"template_id": "welcome_v1",
"template_variables": {
"1": "{{contact.first_name}}"
}
}
}
}
},
{
"id": "wait_one_day",
"type": "delay",
"config": { "duration_seconds": 86400 }
},
{
"id": "tutorial_link",
"type": "create_tracked_link",
"config": {
"target_url": "https://example.com/tutorial",
"output_var_name": "tutorial_url"
}
},
{
"id": "send_tutorial",
"type": "send",
"config": {
"channel_chain": ["whatsapp"],
"channel_content": {
"whatsapp": {
"body_text": "Hey {{contact.first_name}}, here's your tutorial: {{vars.tutorial_url}}"
}
}
}
},
{
"id": "did_they_click",
"type": "condition",
"config": {
"rules": [
{ "field": "flow.var.tutorial_url", "operator": "link_clicked", "value": "" }
],
"rules_op": "all",
"wait_max_seconds": 86400
}
},
{
"id": "thank_clicker",
"type": "send",
"config": {
"channel_chain": ["whatsapp"],
"channel_content": {
"whatsapp": { "body_text": "Glad you checked out the tutorial!" }
}
}
},
{
"id": "nudge_non_clicker",
"type": "send",
"config": {
"channel_chain": ["whatsapp"],
"channel_content": {
"whatsapp": { "body_text": "If you have questions, just reply." }
}
}
}
],
"edges": [
{ "from": "trigger", "to": "welcome", "branch": "default" },
{ "from": "welcome", "to": "wait_one_day", "branch": "default" },
{ "from": "wait_one_day", "to": "tutorial_link", "branch": "default" },
{ "from": "tutorial_link", "to": "send_tutorial", "branch": "default" },
{ "from": "send_tutorial", "to": "did_they_click", "branch": "default" },
{ "from": "did_they_click", "to": "thank_clicker", "branch": "yes" },
{ "from": "did_they_click", "to": "nudge_non_clicker", "branch": "no" }
]
}
Start a run for a new signup:
curl -X POST "https://api.sendseven.com/api/v1/flows/$FLOW_ID/runs" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "contact_id": "'$CONTACT_ID'" }'
2. Bot-style conversation: collect name + route
Use Collect Input to ask the contact a question, store the reply, and branch.
{
"schema_version": 1,
"trigger": { "kind": "manual", "event_name": null },
"entry_node_id": "greet",
"nodes": [
{
"id": "greet",
"type": "send",
"config": {
"channel_chain": ["telegram"],
"channel_content": { "telegram": { "body_text": "Hi! What's your first name?" } }
}
},
{
"id": "ask_name",
"type": "collect_input",
"config": {
"prompt": "Please reply with your name.",
"output_var_name": "first_name",
"channel_chain": ["telegram"],
"timeout_seconds": 600
}
},
{
"id": "save_name",
"type": "tag",
"config": {
"set_fields": { "first_name": "{{vars.first_name}}" }
}
},
{
"id": "thanks",
"type": "send",
"config": {
"channel_chain": ["telegram"],
"channel_content": {
"telegram": { "body_text": "Thanks {{vars.first_name}}, an agent will be with you shortly." }
}
}
},
{
"id": "open_conv",
"type": "open_conversation",
"config": {
"channel_type": "telegram",
"internal_note": "Bot collected name: {{vars.first_name}}"
}
}
],
"edges": [
{ "from": "trigger", "to": "greet", "branch": "default" },
{ "from": "greet", "to": "ask_name", "branch": "default" },
{ "from": "ask_name", "to": "save_name", "branch": "default" },
{ "from": "save_name", "to": "thanks", "branch": "default" },
{ "from": "thanks", "to": "open_conv", "branch": "default" }
]
}
While the run is parked on Collect Input, the contact's reply on Telegram is consumed by the flow and not surfaced as an inbox conversation. After open_conversation, replies behave normally.
3. Abandoned-cart recovery
Webhook from your e-commerce platform → start a flow with cart context → send a reminder, wait 6h, offer a discount, wait another 24h, finally reach out via email.
curl -X POST "https://api.sendseven.com/api/v1/flows/$FLOW_ID/runs" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "'$CONTACT_ID'",
"idempotency_key": "abandoned-cart:'$CART_ID'",
"trigger_payload": {
"cart_id": "'$CART_ID'",
"cart_total": "129.50",
"checkout_url": "https://shop.example.com/checkout/'$CART_ID'"
}
}'
Inside the flow, {{vars.checkout_url}} and {{vars.cart_total}} are available in every Send node. The idempotency_key ensures retries from your e-commerce webhook don't spawn duplicate flows.
A condensed graph for this flow (full JSON omitted for brevity):
trigger → send_reminder_whatsapp → wait_6h → check_recovered (condition: order_completed)
├─ yes → stop
└─ no → send_discount_whatsapp → wait_24h → check_recovered_again (condition)
├─ yes → stop
└─ no → send_final_email
The check_recovered condition can use a Webhook node to call your platform's order-status API and branch on the response.
4. Birthday greeting on a schedule
Use the schedule trigger, which is evaluated every minute and supports both cron-style and birthday-anchored rules — no external cron needed.
If you'd rather drive it yourself, give the flow a manual trigger and POST /flows/{id}/runs from your own scheduler. Standalone scheduled sends (without a flow) are also available directly via the Scheduler API: POST /api/v1/scheduler/sends for one-offs and POST /api/v1/scheduler/rules for recurring birthday, recurring_cron, and segment_blast rules.
5. Trigger flow from Stripe webhook
A common integration: when Stripe fires checkout.session.completed, start a "thank you + onboarding" flow.
# pseudocode in your Stripe webhook handler
import requests
def handle_stripe_webhook(event):
if event["type"] != "checkout.session.completed":
return
session = event["data"]["object"]
contact_id = lookup_contact_by_stripe_customer(session["customer"])
if not contact_id:
return
requests.post(
f"https://api.sendseven.com/api/v1/flows/{ONBOARDING_FLOW_ID}/runs",
headers={"Authorization": f"Bearer {API_TOKEN}"},
json={
"contact_id": contact_id,
"idempotency_key": f"stripe-checkout:{session['id']}",
"trigger_payload": {
"amount_paid": str(session["amount_total"] / 100),
"currency": session["currency"].upper(),
"receipt_url": session.get("receipt_url"),
},
},
timeout=10,
)
The idempotency_key derived from Stripe's session ID makes the call safe to retry.
6. Stop a run from the contact side
Two ways a contact can opt out of an in-flight flow:
- STOP keyword — sending the literal text
STOP(orUNSUBSCRIBE,OPT OUT, configurable per flow underinbound_behavior.stop_keywords) to any channel where the flow has sent a message will cancel the run and unsubscribe the contact from that channel. - Stop URL — every Send node has access to
{{flow.stop_url}}, which renders an HMAC-signed URL likehttps://c.sendseven.com/uf/<token>. Visiting it cancels the run and shows a confirmation page.
Both paths emit a flow.run.failed webhook with reason: "stopped_by_contact".