Workflow recipes
Six workflows to copy and adapt: Slack alerts, status emails, push notifications, a nightly clean-up, partner webhooks and chained workflows.
Last updated
Each recipe is a complete workflow you can rebuild and change. They cover the patterns that come up most: telling people when something happens, cleaning up on a schedule, accepting data from another system, and having one workflow hand over to another. Every recipe is shown in the Anythink dashboard, with the CLI and with an AI assistant over MCP.
New to workflows? Start with Build your first workflow, and see How workflows work for the model. Expressions such as {{ $anythink.trigger.data.email }} are explained in Template syntax.
| Recipe | Trigger | Steps |
|---|---|---|
| Notify Slack about a new lead | A record is created | Integration |
| Email a customer when a ticket is resolved | A field changes to a value | Send an Email |
| Send a push when an order ships | A field changes to a value | Send a Push Notification |
| Clear out expired records every night | A schedule | Delete Data |
| Create or update a record from a partner's webhook | An API call | Upsert Data |
| Chain workflows with a record write | A field changes, then a record is created | Create Data, Update Data |
Note: These examples use entity and field names such as leads and status. Swap in your own. A step's parameters name the entity as entity_name, except Read Data, which uses entity.
Notify Slack about a new lead
Goal: post a message in #sales whenever someone is added to leads.
Before you start: connect Slack to your project. See Connect Slack to send messages from workflows. You need a leads entity with name and company text fields.
| Key | Step | Why |
|---|---|---|
post_to_slack |
Integration | Runs Slack's Send Message operation |
In the Anythink dashboard
- Open Workflows and select New Workflow.
- Enter the Name
notify-new-lead. Leave Trigger Type as Event, set Event Type to Entity Created and Entity toleads. - Select Create Workflow, then Add Step and choose Integration.
- Enter the Name
Post to Slackand the Keypost_to_slack, and tick Enable Step. - Choose the Slack provider and the Send Message operation. Enter the channel
#salesand this message:
New lead: {{ $anythink.trigger.data.name }} from {{ $anythink.trigger.data.company | default: "an unknown company" }}
- Save the step, make it the start step, save the workflow, then select Enable Workflow.
With the CLI
anythink workflows create notify-new-lead \
--trigger Event --event EntityCreated --entity leads --enabled
anythink workflows step-add <workflow_id> post_to_slack \
--name "Post to Slack" --action Integration --start --enabled \
--params '{
"provider": "slack",
"operation": "send-message",
"credential_source": "system",
"inputs": {
"channel": "#sales",
"text": "New lead: {{ $anythink.trigger.data.name }} from {{ $anythink.trigger.data.company | default: '"'"'an unknown company'"'"' }}"
}
}'
The workflows create command prints the workflow's id. Use it in step-add.
With an AI assistant (MCP)
Create a workflow called notify-new-lead that runs when a record is created in leads. Add an Integration step with the key post_to_slack that sends a Slack message to #sales saying "New lead: " followed by the lead's name, " from " and its company, or "an unknown company" if it's empty. Make it the start step and enable the workflow.
The assistant runs workflows create and workflows step-add through the cli tool.
How it works:
credential_source: "system"posts as your project's Slack connection, not as a particular user.| defaultcovers a lead with no company, so the message never shows a raw{{ }}.- The step output has
ok: truewhen Slack accepted the message. Open View Job History to see it. - If Slack isn't connected, the step fails and the job is marked Failed with a message that says so. Link the step's on failure handle to a Send an Email step if you want to hear about it.
Email a customer when a ticket is resolved
Goal: send the requester an email the moment a ticket's status becomes resolved, and only then.
Before you start: verify a sending domain and create an email template with the type ticket_resolved that uses the variables {{ first_name }} and {{ subject }}. See Send email from a workflow. You need a tickets entity with requester_email, first_name, subject and status text fields.
| Key | Step | Why |
|---|---|---|
send_resolved |
Send an Email | Sends the template to the requester |
The trigger carries a filter, so editing a resolved ticket later doesn't send the email again.
In the Anythink dashboard
- Open Workflows and select New Workflow. Name it
ticket-resolved. - Set Event Type to Entity Updated and Entity to
tickets. - Under Fire only when…, select Add filter. Set the field to
status, the operator to changed to and the value toresolved. - Select Create Workflow, then Add Step and choose Send An Email. Name it
Email requesterwith the Keysend_resolved. - Enter
{{ $anythink.trigger.data.requester_email }}as the To Email Address and pick theticket_resolvedtemplate. - On the Mappings tab, set
first_nameto{{ $anythink.trigger.data.first_name }}andsubjectto{{ $anythink.trigger.data.subject }}. - Save the step, make it the start step, save the workflow and select Enable Workflow.
With the CLI
anythink workflows create ticket-resolved \
--trigger Event --event EntityUpdated --entity tickets \
--filter '{"field":"status","op":"changed_to","value":"resolved"}' \
--enabled
anythink workflows step-add <workflow_id> send_resolved \
--name "Email requester" --action SendAnEmail --start --enabled \
--params '{
"to": "{{ $anythink.trigger.data.requester_email }}",
"template_type": "ticket_resolved",
"payload": "{\"first_name\":\"{{ $anythink.trigger.data.first_name }}\",\"subject\":\"{{ $anythink.trigger.data.subject }}\"}"
}'
payload is a string that holds JSON, so its inner quotes are escaped.
With an AI assistant (MCP)
Create a workflow called ticket-resolved that runs when a record in tickets is updated and its status changes to resolved. Add a Send An Email step with the key send_resolved that emails the ticket's requester_email using the ticket_resolved template, with first_name and subject filled from the ticket. Make it the start step and enable the workflow.
The assistant runs workflows create with a filter, then workflows step-add, through the cli tool.
How it works:
changed_tocompares the record before and after the update. It matches once, whenstatusmoves toresolved, not on every later edit.payloadsupplies the template's own variables. A key the template doesn't use is ignored.- The step queues one email for one recipient. It refuses to send if any value still contains
{{, so a typo fails the job instead of emailing a placeholder.
Send a push when an order ships
Goal: tell the customer when their order's status becomes shipped.
Before you start: set up push delivery and register your app's devices. See Push notifications from a workflow. You need an orders entity with a status text field and a user_id integer field holding the customer's user id.
| Key | Step | Why |
|---|---|---|
notify |
Send a Push Notification | Sends to the customer's devices |
In the Anythink dashboard
- Open Workflows and select New Workflow. Name it
order-shipped. - Set Event Type to Entity Updated and Entity to
orders. - Under Fire only when…, add a filter:
status, changed to,shipped. - Select Create Workflow, then Add Step and choose Send a Push Notification. Give it the Key
notify. - Choose Custom content. Set the Title to
Order {{ $anythink.trigger.id }} has shippedand the Body toTap to track your delivery. - Under Send to, choose A single user and enter
{{ $anythink.trigger.data.user_id }}as the User id. - Save the step, make it the start step, save the workflow and select Enable Workflow.
With the CLI
anythink workflows create order-shipped \
--trigger Event --event EntityUpdated --entity orders \
--filter '{"field":"status","op":"changed_to","value":"shipped"}' \
--enabled
anythink workflows step-add <workflow_id> notify \
--name "Push customer" --action SendPushNotification --start --enabled \
--params '{
"title": "Order {{ $anythink.trigger.id }} has shipped",
"body": "Tap to track your delivery.",
"click_action": "myapp://orders/{{ $anythink.trigger.id }}",
"data": { "order_id": "{{ $anythink.trigger.id }}" },
"audience": { "kind": "user", "user_id": "{{ $anythink.trigger.data.user_id }}" }
}'
With an AI assistant (MCP)
Create a workflow called order-shipped that runs when a record in orders is updated and its status changes to shipped. Add a Send a Push Notification step with the key notify that sends "Order" plus the order's id plus "has shipped" to the user in the order's user_id. Make it the start step and enable the workflow.
The assistant runs workflows create and workflows step-add through the cli tool.
How it works:
- On an update,
$anythink.trigger.idis the order's id and$anythink.trigger.datais the updated record, souser_idcomes straight from the order. click_actionanddatatravel with the notification, and your app decides what to do with them.- The step waits for delivery and reports how many devices it reached. It fails if there is nobody to send to, such as a customer with no registered device, so link its on failure handle if that's a normal outcome.
Clear out expired records every night
Goal: delete every row in sessions whose expires_at has passed, at 03:00 UTC each day.
Before you start: you need a sessions entity with an expires_at timestamp field.
| Key | Step | Why |
|---|---|---|
purge |
Delete Data | Removes every record matching the filter |
In the Anythink dashboard
- Open Workflows and select New Workflow. Name it
purge-expired-sessions. - Set Trigger Type to Timed. Select Use cron expression and enter
0 3 * * *. - Select Create Workflow, then Add Step and choose Delete Data. Give it the Key
purge. - Set Entity to
sessions. Select Add Filter Condition and set the field toexpires_at, the operator to less than and the value to{{ $anythink.now }}. - Save the step, make it the start step, save the workflow and select Enable Workflow.
- To try it now, select Test Workflow in the editor.
With the CLI
anythink workflows create purge-expired-sessions \
--trigger Timed --cron "0 3 * * *" --enabled
anythink workflows step-add <workflow_id> purge \
--name "Delete expired sessions" --action DeleteData --start --enabled \
--params '{
"entity_name": "sessions",
"filter_conditions": [
{ "field": "expires_at", "operator": "lt", "value": "{{ $anythink.now }}" }
]
}'
anythink workflows trigger <workflow_id>
anythink workflows jobs <workflow_id>
The last two commands run the workflow once now and show its job.
With an AI assistant (MCP)
Create a workflow called purge-expired-sessions that runs every day at 03:00 UTC. Add a Delete Data step with the key purge that deletes records from sessions where expires_at is earlier than the current time. Make it the start step and enable the workflow, then run it once and show me the job.
The assistant runs workflows create, workflows step-add, workflows trigger and workflows jobs through the cli tool.
How it works:
- Schedules are in UTC and checked once a minute. A run that was missed isn't made up later.
{{ $anythink.now }}is read when the step runs, so each night compares against that moment.- Delete Data removes every match and requires at least one filter condition, so it can't empty an entity by accident.
- Preview the filter first. Use a Read Data step with the same condition, or run the equivalent
anythink data listcommand, and check what comes back. - A scheduled job has no record behind it, so
$anythink.trigger.datais empty. Everything the step needs comes from its own parameters.
Create or update a record from a partner's webhook
Goal: accept orders from a partner's system over HTTP, creating a record for a new order and updating the record when the partner sends the same order again.
Before you start: you need a partner_orders entity with a unique partner_ref text field, plus status, total and customer_email fields. Give the partner its own API key with only the anythink_workflows:trigger permission. See Webhooks and API keys.
| Key | Step | Why |
|---|---|---|
upsert_order |
Upsert Data | Matches on the partner's reference, then updates or creates |
In the Anythink dashboard
- Open Workflows and select New Workflow. Name it
partner-order. - Set Trigger Type to API Call and enter the API Route
partner-order, without a leading slash. - Select Create Workflow, then Add Step and choose Upsert Data. Give it the Key
upsert_order. - Set Entity to
partner_orders, Match On topartner_ref, On Match to Update and Update Fields tostatusandtotal. - Set the Payload:
{
"partner_ref": "{{ $anythink.trigger.data.order_id }}",
"status": "{{ $anythink.trigger.data.status | default: \"received\" }}",
"total": "{{ $anythink.trigger.data.total }}",
"customer_email": "{{ $anythink.trigger.data.customer.email }}"
}
- Save the step, make it the start step, save the workflow and select Enable Workflow.
With the CLI
anythink workflows create partner-order \
--trigger Api --api-route partner-order --enabled
anythink workflows step-add <workflow_id> upsert_order \
--name "Upsert order" --action UpsertData --start --enabled \
--params '{
"entity_name": "partner_orders",
"match_on": ["partner_ref"],
"on_match": "Update",
"update_fields": ["status", "total"],
"payload": "{\"partner_ref\":\"{{ $anythink.trigger.data.order_id }}\",\"status\":\"{{ $anythink.trigger.data.status | default: '"'"'received'"'"' }}\",\"total\":\"{{ $anythink.trigger.data.total }}\",\"customer_email\":\"{{ $anythink.trigger.data.customer.email }}\"}"
}'
Test it the way the partner will, with the route and a key:
curl -X POST "https://api.my.anythink.cloud/org/$PROJECT_ID/workflows/api/partner-order" \
-H "x-api-key: $ANYTHINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ord_1001","total":42.5,"customer":{"email":"ada@example.com"}}'
Expect 204 No Content. Send it again with "status":"shipped" and "total":45. There is still one record, now with the new status and total.
With an AI assistant (MCP)
Create a workflow called partner-order with an API trigger on the route partner-order. Add an Upsert Data step with the key upsert_order for partner_orders that matches on partner_ref and, on a match, updates status and total. Fill the record from the request body: order_id into partner_ref, status (default "received"), total and customer.email. Make it the start step and enable the workflow.
The assistant runs workflows create and workflows step-add through the cli tool.
How it works:
- The JSON body the partner sends becomes
$anythink.trigger.data, and paths such ascustomer.emailreach into it. match_onmakes the webhook safe to repeat: the sameorder_idupdates its record and never creates a second one. The uniquepartner_reffield backs that up at the database.update_fieldslimits what a repeat call can overwrite, so edits your team makes to other fields survive.- The response is
204as soon as the job is queued. It doesn't say whether the upsert worked, so check the workflow's job history. If the partner needs an answer, have it read the record afterwards.
Chain workflows with a record write
Goal: when an order is paid, create an invoice record. Then a second workflow notices the new invoice and marks the order as invoiced.
Before you start: you need an orders entity with total (decimal), status (text) and invoiced (boolean) fields, and an invoices entity with order_id (integer) and amount (decimal).
There is no step that calls another workflow. Instead, a Create Data or Update Data step writes a record, and that write starts any workflow with an event trigger on the entity. Two workflows hand over through the data.
| Workflow | Trigger | Step |
|---|---|---|
raise-invoice |
orders updated, status changed to paid |
Create Data on invoices |
mark-invoiced |
invoices created |
Update Data on orders |
In the Anythink dashboard
- Create the workflow
raise-invoice. Set Event Type to Entity Updated and Entity toorders. Under Fire only when…, addstatus, changed to,paid. - Add a Create Data step with the Key
create_invoice. Set Entity toinvoicesand the Payload:
{
"order_id": "{{ $anythink.trigger.id }}",
"amount": "{{ $anythink.trigger.data.total }}"
}
- Make it the start step, save, and select Enable Workflow.
- Create a second workflow,
mark-invoiced. Set Event Type to Entity Created and Entity toinvoices. - Add an Update Data step with the Key
mark_invoiced. Set Entity toorders, Record ids to{{ $anythink.trigger.data.order_id }}and the Payload to{ "invoiced": true }. - Make it the start step, save, and select Enable Workflow.
With the CLI
anythink workflows create raise-invoice \
--trigger Event --event EntityUpdated --entity orders \
--filter '{"field":"status","op":"changed_to","value":"paid"}' --enabled
anythink workflows step-add <raise_invoice_id> create_invoice \
--name "Create invoice" --action CreateData --start --enabled \
--params '{"entity_name":"invoices","payload":"{\"order_id\":\"{{ $anythink.trigger.id }}\",\"amount\":\"{{ $anythink.trigger.data.total }}\"}"}'
anythink workflows create mark-invoiced \
--trigger Event --event EntityCreated --entity invoices --enabled
anythink workflows step-add <mark_invoiced_id> mark_invoiced \
--name "Mark order invoiced" --action UpdateData --start --enabled \
--params '{"entity_name":"orders","ids":"{{ $anythink.trigger.data.order_id }}","payload":"{\"invoiced\":true}"}'
Try it: create an order, then set its status to paid.
anythink data create orders --data '{"customer_email":"sam@example.com","total":149.50,"status":"new","invoiced":false}'
anythink data update orders 1 --data '{"status":"paid"}'
anythink data list invoices
anythink data get orders 1
Within a few seconds invoices has one row with amount 149.5 and order 1 shows invoiced as true. Both workflows have one successful job.
With an AI assistant (MCP)
Create two workflows. The first, raise-invoice, runs when a record in orders is updated and its status changes to paid, and has a Create Data step that adds an invoices record with order_id set to the order's id and amount set to its total. The second, mark-invoiced, runs when an invoices record is created and has an Update Data step that sets invoiced to true on the order whose id is the invoice's order_id. Enable both.
The assistant runs workflows create and workflows step-add for each workflow through the cli tool.
How it works:
$anythink.trigger.idis the order's id, andamountkeeps its number type because the template is the whole value.- In
mark-invoiced, the new invoice is the trigger data, soorder_idleads back to the order. - The second workflow updates
orders, which is the entity the first one watches. Thechanged to paidfilter is what stops a loop: the update changesinvoiced, notstatus, soraise-invoicedoesn't start again. - Always give a workflow that writes to its own trigger entity a filter that its write can't satisfy. A workflow also has a limit of 250 steps per job.
- The two jobs run on their own, so there is no guaranteed order and no shared failure. Each has its own entry in View Job History.
Next steps
- Triggers and scheduling: events, filters, schedules, manual buttons and API routes
- Step types: every step, parameter and output
- Template syntax: every
{{ }}expression a step accepts - Webhooks: receive HTTP calls and call other systems
- Secrets: keep API keys out of your steps
- Connect Slack to send messages from workflows: connect a workspace
- Send email from a workflow: domains, templates and attachments
- Push notifications from a workflow: set up delivery and audiences
- Test workflows and read job history: read jobs and fix failures