powered by

How workflows work

Workflows run your backend logic in Anythink. A trigger starts a job, the job follows a chain of steps, and each step can use what came before it.

Last updated

Workflows are Anythink's automation engine. Use them for the logic that would otherwise need a backend service: reacting to new records, running nightly jobs, exposing an endpoint that does several things at once, calling other APIs, and sending email or push notifications. Nothing to host, and no server code.

This page explains the model and what happens at runtime. To build one, start with Build your first workflow.

The moving parts

Part What it is
Workflow A named automation with one or more triggers and a set of steps. Names are unique within a project.
Trigger What starts the workflow: a data event, a schedule, a button or an API call. A workflow can have several, and any of them starts the same steps.
Step One action: read data, create a record, call an API, run a script, send a notification. Each step has a key, which is how later steps refer to its output.
Job One run of a workflow. It records the payload that started it, every step's log and output, and the final status.

Steps are connected, not just ordered. Each step has an on success link and an optional on failure link to another step. One step is the start step. A job begins there and follows links until a step has nowhere left to go. In the editor, the green handle on a step is on success and the red handle is on failure.

What happens when a workflow runs

  1. A trigger fires. A record is created or updated, a schedule comes due, someone runs the workflow by hand, or your code calls its API route.
  2. Anythink creates a job with the status Pending and a payload of { id, entity_name, data }, then queues it for the workflow worker. For an event trigger with a filter, the filter is checked first. If it doesn't match, no job is created.
  3. The worker picks the job up. It checks your plan's workflow quota and sets the job to Running.
  4. It runs the start step. Templates in the step's parameters, such as {{ $anythink.trigger.data.email }}, are resolved just before it runs.
  5. The step's output is saved. Later steps can read it as $anythink.steps.<key>.data.
  6. The worker follows a link. A successful step follows on success. A failed step follows on failure.
  7. The job finishes. See Success and failure paths.

Jobs run on a separate worker, not in your request. A record you create returns straight away, and the job follows moments later.

Triggers

Trigger Starts a job when Typical use
Event A record is created or updated, a user registers or is invited, a payment or subscription changes, or a push notification button is tapped Welcome emails, follow-up tasks, syncing data
Timed A cron schedule comes due Nightly imports, weekly reports, reminders
Manual Someone runs it from a record in the dashboard, or your code calls the trigger endpoint Admin actions, one-off fixes
API call A POST reaches /org/{project_id}/workflows/api/{route} Custom endpoints for your app

A trigger that watches an entity must name an entity that exists. The workflow has an Enable Workflow setting, and each trigger has its own Enabled setting, so you can switch one trigger off without touching the others.

Event triggers

Pick an Event Type. The dashboard shows the names below with spaces, and the CLI and API use them as written.

Event Fires when What the job receives
EntityCreated, EntityUpdated A record in the chosen entity is created or updated $anythink.trigger.id is the record's id and entity_name is the entity. The data is the record.
UserRegistered, UserInvited A user registers with your project or is invited to it $anythink.trigger.id is the user's id. The data has first_name, last_name, email and referral_code.
PaymentCreated, PaymentSucceeded, PaymentFailed AnythinkPay records a payment The payment. $anythink.trigger.id is 0.
SubscriptionCreated, SubscriptionActivated, SubscriptionExpired A subscription is created, activated or expires The subscription. $anythink.trigger.id is 0.
PaymentMethodCaptured, PaymentMethodCaptureFailed, PaymentMethodRemoved A saved payment method is captured, fails to capture or is removed The payment method. $anythink.trigger.id is 0.
PushActionTaken Someone taps a button on a push notification The button's group, the action, the user and any extra data the app sent. See Push notifications from a workflow.

On a record event, the trigger data doesn't include the id, so read the record's id from $anythink.trigger.id. Payment and subscription events are raised for Stripe activity. See Webhooks for the details.

An event trigger can carry a filter so the workflow only runs when it matters, for example only when an order's status changes to paid. A filter compares a field with a value using =, ≠, >, ≥, <, ≤, in, contains, is null or is not null. On an update it can also test whether a field changed or changed to a value. Conditions combine with All of these or Any of these. Details are in Triggers and scheduling.

Timed triggers

A timed trigger uses a standard five-field cron expression, such as 0 6 * * * for 06:00 every day. Schedules are in UTC, and Anythink checks for due schedules once a minute, so the finest resolution is one minute. A scheduled job has no record behind it: $anythink.trigger.id and $anythink.trigger.data are empty. Each due schedule starts one job, and missed runs aren't caught up.

Manual triggers

A manual trigger lists the entities it applies to. Those entities' records get a button in the Anythink dashboard that runs the workflow on that record. Your code can do the same by calling the workflow's trigger endpoint, which needs the anythink_workflows:trigger permission and takes the entity name, record id and data you send.

API call triggers

An API call trigger gives the workflow its own route. Callers POST JSON to it with an API key, the body becomes $anythink.trigger.data, and the response is 204 No Content as soon as the job is queued. The request doesn't wait for the result. Webhooks has the URL, the rules for routes, the permission the caller needs and an end-to-end example. To create a key for the caller, see API keys.

Steps

Each step has a name, a key and an action. The key can contain letters, numbers and underscores, and later steps use it in templates. The actions are:

Step What it does
Read Data Reads records from an entity, with filters, sorting and a limit
Create Data Creates one record, or one per item in a list
Update Data Updates records by id or by filter
Upsert Data Matches records on key fields, then updates, ignores or creates
Delete Data Deletes every record matching a filter
Condition Branches the workflow on one or more comparisons
Run Script Runs JavaScript against the trigger data and earlier step outputs
Call An API Sends an HTTP request and keeps the JSON response. See Webhooks.
Send an Email Sends one of your project's email templates. See Send email from a workflow.
Send a Push Notification Sends to a user, a list of users, a record's owners or everyone, once or per row. See Push notifications from a workflow.
File Handler Downloads a file into Anythink and attaches it to a record, links an existing file, or detaches one
Integration Calls a connected service: Claude, OpenAI, Grok, Slack, GitHub, Google Calendar, LinkedIn or X. See How integrations work.

Every parameter and output is listed in Step types. For ready-made patterns, see Workflow recipes.

Passing data between steps

Parameters can contain templates in double braces. Anythink resolves them just before the step runs.

Template Value
{{ $anythink.trigger.id }} The id of the record or user that fired the trigger
{{ $anythink.trigger.data.email }} A field from the trigger data. Use dots for nested objects and square brackets for list items, such as items[0].sku.
{{ $anythink.steps.find_customer.data[0].name }} A field from an earlier step's output. Steps that return records or script results put a list under data, so use data[0] for the first item.
{{ $anythink.secrets.PARTNER_API_KEY }} A project secret, looked up when the step runs. See Secrets.
{{ $anythink.now }} The current time, in UTC, in ISO 8601 format

When a template is the whole value of a JSON field, the value keeps its type, so a number stays a number and an object stays an object. Inside longer text it's written into the text. If a path doesn't resolve, the expression is left as written, so add a fallback with | default:

text
{{ $anythink.trigger.data.nickname | default: "there" }}

The full rules are in Template syntax.

Success and failure paths

A step succeeds when its action completes and fails when it doesn't. A failure might be a rejected parameter, a script error, or a non-2xx response from a Call An API step. A Condition step that evaluates to false counts as a failure too, which is how you branch: link on success to the "true" path and on failure to the "false" path.

What happens Job status
The last step that ran succeeded Success
A step failed and its on failure link led to steps that finished Success. The failed step is still marked failed in the history.
A step failed and has no on failure link Failed, with that step's error message
The start step is missing, a link points to a step that no longer exists, or the job runs more than 250 steps Failed, with a message saying which
Your plan's workflow quota is used up Failed, with a quota message. The run isn't counted.

Plan the "nothing to do" outcome. If a Condition is false and has no on failure link, the job ends Failed. Link the false branch to a step, even a harmless one, when that outcome is normal.

Retries

Anythink doesn't retry anything. A failed step either follows its on failure link or ends the job, and a failed job stays failed. To run it again, trigger the workflow again with the same payload. The job history keeps the payload you need. To react to a failure instead, send yourself an email or a message from the on failure path.

Jobs and history

Every run is recorded in the workflow's Job History, so you can see exactly what each step received and returned.

Job status Meaning
Pending Queued, waiting for the worker
Running The worker is executing steps
Success Finished as described above
Failed A step failed, or the job couldn't complete

A job keeps the payload that started it, an error message if it failed, and for every step that ran its success flag, log, output and error. Open it in the Anythink dashboard from View Job History in the workflow editor, or list a workflow's jobs with anythink workflows jobs <workflow_id>. To try a workflow without waiting for its trigger, use Test Workflow in the editor, or run anythink workflows trigger <workflow_id> --payload '<json>'. The payload appears as $anythink.trigger.data. Test workflows and read job history covers reading jobs and tracking down failures.

Who a workflow acts as

Workflow steps run as the Anythink workflow worker, a trusted service, not as the person who started the job. Design for two consequences:

  • Reads ignore row-level security. A Read Data step sees every record in the entity. Filter explicitly to the user or group you're acting for, for example user_id equals {{ $anythink.trigger.data.user_id }}.
  • Records created by a workflow have no owner. On entities with row-level security, grant access in the step itself with its row-level security options. See Step types and Groups and row-level security.

Anyone who can edit a workflow can therefore read and send any data in the project. Give the workflow permissions (anythink_workflows:create, :update, :delete and :trigger) only to people you'd trust with that access. See Roles and permissions.

Limits

Limit Value
Steps run per job 250. A job that loops past this fails with a maximum-step-limit message.
Retries None. See Retries.
Schedules Five-field cron, UTC only, one-minute resolution.
Run Script 30 seconds, 64 MB and 100,000 statements per run. No network access.
Call An API 30 seconds per request. http and https addresses on the public internet only.
File downloads 50 MB and 30 seconds per file.
Runs per month Set by your plan's workflow quota.
Names A workflow name is unique in the project and up to 255 characters. A step key is up to 255 characters.
API call responses 204 No Content. The job runs in the background, and the response doesn't include its result.
Job order Not guaranteed. Jobs run concurrently.
Sub-workflows Not supported. Put shared logic in each workflow, or have one workflow write a record that another workflow's event trigger watches.

Behaviour worth knowing

  • Workflows can trigger workflows. Create Data and Update Data steps write through the normal API, so they fire event triggers, including on the entity the workflow itself watches. Use a trigger filter to stop a workflow from starting itself again.
  • A step that runs twice replaces its output. If your links loop back to a step, later steps see the most recent run's output.
  • Several workflows can watch the same event. Each one starts its own job.

Next steps