powered by

Automation

Workflows

Updated Mar 19, 2026
Anythink Workflows let you automate any business process without writing backend code. Connect your data to the outside world, react to events in real time, run scheduled jobs, and build complex multi-step logic through a visual drag-and-drop designer.

What can workflows do?

Workflows are the automation engine inside Anythink. They can:

  • React to data changes — send a welcome email when a user signs up, create a task when an order is placed
  • Run on a schedule — sync data from an external API every morning, generate a weekly report
  • Expose custom API endpoints — build serverless-style functions that run your business logic on demand
  • Call external services — send messages, post to webhooks, call any HTTP API
  • Process and transform data — filter, enrich, and reshape data as it flows through your system
  • Run custom JavaScript — execute scripts for logic that cannot be expressed as a simple step

Creating a workflow

  1. Open your Anythink dashboard and go to Workflows in the left menu
  2. Click New Workflow
  3. Give it a name and choose a trigger type
  4. Add steps by clicking the + button on the canvas
  5. Connect steps by dragging from the output of one to the input of the next
  6. Click Save, then Enable when you are ready to go live

The canvas

The workflow canvas works like a whiteboard. Drag steps onto it, connect them, and the workflow executes from left to right.

  • Success paths (green connectors) — followed when a step completes successfully
  • Failure paths (red connectors) — followed when a step errors, if you have connected one
  • Condition branches — a Condition step has two outputs: one when conditions are met, one when they are not

Click any step to open its configuration panel on the right.


Trigger types

There are four ways to start a workflow. See Triggers and Scheduling for full details.

Trigger When it fires
Timed On a cron schedule, e.g. every day at 6am
Event When a record is created, updated, or deleted
Manual On demand from the dashboard or CLI
API Endpoint When an HTTP request hits your custom route

Step types

Each step does one focused thing. Chain them together to build complex logic. See Step Types for full details.

Step What it does
Read Data Fetch records from an entity
Create Data Insert one or more records into an entity
Update Data Modify an existing record
Delete Data Remove a record
Call an API Make an HTTP request to any external service
Run Script Execute JavaScript for custom logic
Condition Branch the workflow based on a value

Passing data between steps

Every step produces output that subsequent steps can reference using template syntax:

text
{{ $anythink.steps.my_step.data[0].field_name }}

The trigger's data is available as:

text
{{ $anythink.trigger.data.field_name }}

This lets you build pipelines where each step transforms and passes data to the next.


Testing and monitoring

After saving, test a workflow directly from the canvas using the Test button. The results panel shows the exact input and output of every step. All workflow runs are logged in Job History — you can replay, inspect, and debug any run. See Monitoring and Testing for details.


Enabling and disabling

A workflow must be Enabled to run automatically. Toggle this from the dashboard or CLI:

bash
anythink workflows enable 76
anythink workflows disable 76

Disabled workflows can still be triggered manually.

Triggers and Scheduling

Updated Mar 19, 2026
Workflows are started by a trigger. There are four trigger types: a schedule, a data event, a manual run, or an HTTP API call. Choosing the right trigger is the first step in designing any workflow.

Timed (Scheduled)

A timed workflow runs automatically on a schedule defined using a cron expression.

Cron expression format

text
  minute (0-59)
  |  hour (0-23)
  |  |  day of month (1-31)
  |  |  |  month (1-12)
  |  |  |  |  day of week (0-6, Sunday=0)
  *  *  *  *  *

Common examples

Expression Runs
0 6 * * * Every day at 6:00am
0 9 * * 1 Every Monday at 9:00am
0 * * * * Every hour
*/15 * * * * Every 15 minutes
0 0 1 * * First day of every month at midnight
30 8 * * 1-5 Weekdays at 8:30am

Use cases

  • Pulling fresh data from an external API each morning
  • Sending a daily or weekly digest
  • Archiving or cleaning up old records on a schedule
  • Generating periodic reports

Event (Entity Changes)

An event workflow fires automatically when a record in a chosen entity is created, updated, or deleted.

Configuration

When creating the workflow, choose:

  • Entity — which entity to watch (e.g. orders, users, articles)
  • Event type — one of:
    • EntityCreated — a new record was added
    • EntityUpdated — an existing record was changed
    • EntityDeleted — a record was removed

Accessing trigger data

The full record that caused the trigger is available in every step:

text
{{ $anythink.trigger.data.id }}
{{ $anythink.trigger.data.email }}
{{ $anythink.trigger.data.status }}

For EntityUpdated, the trigger data contains the record's current values after the update.

Gating on field values

Event workflows fire on every change to the entity. To act only in certain cases, add a Condition step right after the trigger.

For example, to only send a notification when status becomes approved:

  1. Add a Run Script step to extract a flag:
javascript
var status = $anythink.trigger.data.status;
return { is_approved: status === "approved" ? "yes" : "no" };
  1. Add a Condition step that checks $anythink.steps.check.data[0].is_approved equals yes
  2. Connect the matching output to your notification step

Use cases

  • Send a welcome email when a new user signs up
  • Notify a channel when an order changes to shipped
  • Create a related record automatically when a parent is created
  • Sync a record to an external CRM when it is updated

Manual

A manual workflow has no automatic trigger. It runs only when explicitly started.

Running a manual workflow

From the dashboard: Open the workflow and click Run Now.

From the CLI:

bash
anythink workflows trigger 76
anythink workflows trigger 76 --payload '{"userId": 123, "action": "reset-password"}'

Via the API:

http
POST /org/{orgId}/workflows/{workflowId}/trigger

Accessing the payload

Any JSON payload passed when triggering is available as trigger data:

text
{{ $anythink.trigger.data.userId }}
{{ $anythink.trigger.data.action }}

Use cases

  • One-off data migrations
  • On-demand report generation
  • Admin-triggered operations such as a resend invoice button calling the API
  • Testing a workflow before enabling automatic runs

API Endpoint

An API trigger turns your workflow into a custom HTTP endpoint. When an HTTP request arrives at that URL, the workflow runs.

Setup

Select API as the trigger type and enter a custom route, for example: /send-welcome-email

Anythink creates a POST endpoint at:

http
POST https://your-instance.anythink.cloud/org/{orgId}/workflows/endpoint/send-welcome-email

Accessing the request body

Any JSON body sent in the request is available as trigger data:

text
{{ $anythink.trigger.data.email }}
{{ $anythink.trigger.data.userId }}

Calling your endpoint

bash
curl -X POST \
  https://your-instance.anythink.cloud/org/{orgId}/workflows/endpoint/send-welcome-email \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "name": "Alice"}'

Use cases

  • Receiving webhooks from Stripe, GitHub, or any external service
  • A serverless-style function for your frontend to call
  • A custom action endpoint for Zapier or n8n to trigger
  • Processing form submissions from a static website

Step Types

Updated Mar 19, 2026
Every workflow is made up of steps. Each step does one focused thing — reading or writing data, calling an API, running custom logic, or branching the flow. Here is a complete guide to every available step type.

Read Data

Fetch records from any entity in your project.

Configuration

Field Description
Entity The entity to read from (e.g. customers, orders)
Limit Maximum number of records to return
Filters Field conditions to narrow results (optional)

Accessing results

Read Data returns an array. Access individual fields like this:

text
{{ $anythink.steps.get_users.data[0].name }}
{{ $anythink.steps.get_users.data[0].email }}

You can also pass the full array to a downstream Create Data step for bulk operations.


Create Data

Insert one or more records into an entity.

Single record

Provide a JSON object with the field values. Template syntax is fully supported:

json
{
  "title": "{{ $anythink.steps.generate.data[0].title }}",
  "status": "draft",
  "author": 2
}

Bulk create

To create multiple records at once, pass an array returned by a Run Script step:

text
{{ $anythink.steps.parse_step.data[0].items }}

This creates one record per item in a single operation, without needing a loop.

Accessing the created record

After creating, the new record including its generated id is available to subsequent steps:

text
{{ $anythink.steps.create_order.data[0].id }}

Update Data

Modify an existing record.

Configuration

Field Description
Entity The entity containing the record
Record ID The ID to update — supports template syntax
Fields JSON object with only the fields to change

Example

Update the status of the record that triggered the workflow:

  • Entity: orders
  • Record ID: {{ $anythink.trigger.data.id }}
  • Fields: {"status": "processed"}

Only the fields you specify are changed. Everything else is untouched.


Delete Data

Remove a record from an entity.

Field Description
Entity The entity to delete from
Record ID The ID of the record to delete

Deletions are permanent and cannot be reversed from within a workflow.


Call an API

Make an HTTP request to any external service — a webhook, a third-party API, or any other system.

Configuration

Field Supports templates Notes
URL No Must be a hardcoded URL
Method GET, POST, PUT, PATCH, DELETE
Headers Yes Key-value pairs
Body Yes JSON request body

Important notes

  • The URL field does not support template syntax. It must be a fixed URL. Put variable parts (IDs, slugs) in the request body instead
  • Do not set Content-Type manually. It is set automatically. Including it yourself will cause the request to fail
  • Headers and body fully support {{ $anythink.steps.step_name.data[0].field }} templates

Example: calling an external AI API

Headers:

json
{
  "x-api-key": "{{ secrets.MY_API_KEY }}",
  "anthropic-version": "2023-06-01"
}

Body:

json
{
  "model": "claude-haiku-4-5-20251001",
  "max_tokens": 500,
  "messages": [
    {
      "role": "user",
      "content": "Summarise: {{ $anythink.steps.get_article.data[0].content }}"
    }
  ]
}

Accessing the response

The full response body is available to downstream steps via data[0]:

text
{{ $anythink.steps.call_api.data[0].content[0].text }}

Run Script

Execute a JavaScript function for custom logic that cannot be expressed as a simple step.

Structure

Your script must return a value. The returned value is automatically wrapped in an array by the platform, so it is always accessed as data[0]:

javascript
return { status: "processed", count: 42 };

// Accessed downstream as:
// {{ $anythink.steps.my_script.data[0].status }}
// {{ $anythink.steps.my_script.data[0].count }}

Accessing previous step data

Previous steps data is available via $anythink:

javascript
var items = $anythink.steps.fetch_items.data;
var trigger = $anythink.trigger.data;

var results = [];
for (var i = 0; i < items.length; i++) {
  if (items[i].status === "active") {
    results.push({ id: items[i].id, name: items[i].name });
  }
}

return { items: results, count: results.length };

Important scripting notes

  • Use var declarations and traditional for (var i...) loops. Modern JS features such as optional chaining and for...of may not be supported in all environments
  • Never return a bare empty array. If your script might return no results, use a flag pattern so downstream steps can detect the empty case:
javascript
return {
  items: results,
  has_results: results.length > 0 ? "yes" : "no"
};
  • The platform wraps your return value in an array. Even if you return { x: 1 }, it arrives downstream as [{ x: 1 }] and is accessed via data[0].x
  • Strings containing newlines must be escaped before including in JSON template bodies. Build and format strings in the script, then return them ready to use

Common use cases

  • Parsing and filtering items from an external API response
  • Deduplicating a list of URLs or records before storing
  • Building a prompt string from multiple data sources
  • Computing derived values such as dates, counts, or formatted strings
  • Transforming an array of items to pass to a bulk Create Data step

Condition

Branch the workflow based on the value of a field. A Condition step has two outputs: one path when conditions are met, and another when they are not.

Configuration

Field Description
Logical operator AND or OR — how multiple conditions combine
Conditions One or more field / operator / value checks

Condition operators

Operator Meaning
eq Equals
neq Not equals
gt Greater than
lt Less than
gte Greater than or equal
lte Less than or equal
contains String contains

Referencing fields in conditions

For fields from a Run Script step, which always wraps output in an array:

text
$anythink.steps.my_script.data[0].my_field

For fields from the trigger:

text
$anythink.trigger.data.status

Using Condition as a gate

A common pattern is using a Condition step as a gate — only continue if a condition is true and stop the workflow otherwise. When the condition is not met, the job shows as "Condition evaluation failed" in job history. This is expected behaviour for a gate, not an error.

Dual-gate pattern

When a workflow fetches items from an external source and then filters them, you may end up with nothing at various stages. Use two Condition gates for safety:

  1. gate_items — did the source return any items at all?
  2. gate_results — did any items survive the filter step?

This prevents a downstream Create Data step from receiving an empty array, which can cause unexpected behaviour.

Monitoring and Testing

Updated Mar 19, 2026
Every workflow run is recorded. Anythink's job history gives you full visibility into what ran, when it ran, what each step received and returned, and what went wrong if anything failed. You can also test workflows interactively before enabling them.

Job History

Every time a workflow runs — automatically, manually, or via API — a job record is created. Find it in the Job History tab of any workflow.

The job list shows:

  • Run time — when the workflow started
  • Status — Completed, Failed, or Running
  • Duration — how long the run took
  • Trigger — what started it (schedule, event, manual, API)

Click any job to open the full run detail.


Inspecting a run

The run detail view shows every step that executed, in order.

Step status

  • Completed — the step ran successfully
  • Failed — the step threw an error
  • Skipped — the step was bypassed, for example a Condition gate blocked the path

Step input

The exact data passed into the step, including resolved template values at the moment of execution. This is invaluable for debugging because it shows what {{ $anythink.trigger.data.field }} actually resolved to at runtime.

Step output

The data the step returned. For Read Data steps this is the records fetched. For Run Script this is what your script returned. For Call an API this is the raw response body.

Error details

If a step failed, the error message is shown here.


Understanding "Condition evaluation failed"

When a Condition step's conditions are not met, the job is marked as Failed with the message "Condition evaluation failed". This is normal and expected when you are using a Condition step as a gate.

For example, if a workflow only processes blog posts and a different record triggers it, the Condition gate blocks it. The job shows as failed. This is the intended behaviour, not a bug.


Testing a workflow

You do not need to wait for a real event. Every workflow has a Test button in the canvas toolbar.

Testing a Timed or Manual workflow

Click Test and the workflow runs immediately. Any {{ $anythink.trigger.data.field }} references resolve to empty or null, so make sure your scripts handle missing data gracefully.

Testing an Event workflow

Click Test and provide a sample record as a JSON payload. This simulates what the trigger data would look like for a real event:

json
{
  "id": 42,
  "email": "alice@example.com",
  "status": "approved"
}

Testing an API endpoint workflow

Either click Test and provide a JSON body, or send a real HTTP POST to your endpoint URL. The job appears in history just like any other run.


Re-running a job

From the job detail view, click Re-run to execute the workflow again with the same trigger data as the original run. Useful for:

  • Retrying after a step was misconfigured and has now been fixed
  • Retrying after an external API was temporarily unavailable
  • Processing a record that was accidentally skipped

Common debugging tips

My Run Script step failed.
Open the job detail and click the step. The error section shows the JavaScript error. Common causes are accessing a property on undefined (check the previous step returned data), syntax errors, or unsupported JS syntax such as optional chaining.

My Call an API step returns an unexpected response.
Check the step output for the raw response and HTTP status code. Common issues include a wrong URL, missing auth headers, or an incorrect body format. Remember the URL field does not support template syntax.

My workflow runs but nothing gets created.
Look for a Condition step marked as Skipped. Check whether a Read Data step returned zero records. If you are running an ingest workflow, consider the dual-gate pattern to handle empty results safely.

The step input shows the wrong value.
The issue may be upstream in an earlier step or in how a template was written. Check the output of the step before it to trace where the value changed.

Secrets

Updated Mar 19, 2026
Secrets are encrypted values — API keys, tokens, and passwords — stored securely against your project. They are available to workflows at runtime but their values are never exposed in the dashboard, logs, or API responses.

What secrets are for

Any time a workflow needs to call an external API, it needs credentials — an API key, a bearer token, a webhook secret. Rather than pasting these values directly into your workflow configuration (where they would appear in logs and step outputs), you store them as secrets and reference them by name.

Secrets are encrypted at rest. Only the workflow engine can decrypt them at runtime, and only for the project they belong to.


Managing secrets

In the dashboard

Go to Settings → Secrets in your Anythink dashboard. From here you can:

  • See all your stored secret keys (names only — values are never shown)
  • Add a new secret
  • Rotate (overwrite) an existing secret
  • Delete a secret

Via the CLI

bash
# List all secret keys
anythink secrets list

# Create a new secret (you will be prompted to enter the value securely)
anythink secrets create STRIPE_SECRET_KEY
anythink secrets create ANTHROPIC_API_KEY

# Rotate a secret (overwrites the existing value)
anythink secrets update STRIPE_SECRET_KEY

# Delete a secret
anythink secrets delete STRIPE_SECRET_KEY --yes

Values are entered via a hidden prompt — they are never visible in your terminal history or shell output.


Using secrets in workflows

Reference a secret in any workflow step that supports template syntax using:

text
{{$anythink.secrets.YOUR_KEY_NAME}}

The most common use is in the Call an API step headers or body. For example, to call an external API that requires an API key:

Headers:

json
{
  "Authorization": "Bearer {{$anythink.secrets.MY_SERVICE_TOKEN}}",
  "x-api-key": "{{$anythink.secrets.MY_API_KEY}}"
}

Body:

json
{
  "api_key": "{{$anythink.secrets.STRIPE_SECRET_KEY}}",
  "amount": "{{ $anythink.trigger.data.amount }}"
}

At runtime, the workflow engine decrypts the secret value and substitutes it into the payload before the request is made. The decrypted value never appears in job history or step logs.


Best practices

  • One secret per credential — give each key a clear, descriptive name (STRIPE_SECRET_KEY not KEY1)
  • Rotate regularly — use secrets update to rotate keys when you cycle credentials in external services
  • Delete unused secrets — if a workflow no longer uses a secret, remove it to keep things tidy
  • Never hardcode sensitive values — if you find a token or password pasted directly into a workflow step body, move it to secrets

Email Templates

Updated Mar 19, 2026
Anythink sends transactional emails automatically as part of authentication flows — account confirmation, password recovery, user invitations, and more. Every template can be customised to match your brand and voice.

System templates

Anythink maintains a set of system email templates, one for each triggered event. You can edit the subject line and body content of any template to match your product's tone and branding.

Template When it is sent
Email Confirmation When a new user registers — they must confirm before their account activates
User Invitation When you invite a user to your project via the dashboard or CLI
Password Recovery When a user requests a password reset
Email Change When a user updates their email address
Added to Organisation When a user is added to your organisation

Editing a template

  1. Go to Settings → Email Templates in your Anythink dashboard
  2. Click the template you want to edit
  3. Update the Subject and Body fields
  4. Use the Preview button to see how the rendered email will look with real sample data
  5. Click Save

Changes take effect immediately — the next time that event is triggered, the updated template is used.


Template variables

Each template has access to a set of variables that are automatically substituted at send time. Use double curly braces to insert them:

text
{{ OrgName }}
{{ Email }}
{{ ConfirmationUrl }}
{{ PasswordResetUrl }}
{{ InviteUrl }}
{{ DashboardUrl }}

Commonly used variables:

Variable Description
{{ OrgName }} Your organisation or project name
{{ Email }} The recipient's email address
{{ ConfirmationUrl }} Link the user clicks to confirm their email
{{ PasswordResetUrl }} Link the user clicks to reset their password
{{ InviteUrl }} Link the user clicks to accept an invitation
{{ DashboardUrl }} Direct link to the dashboard
{{ Token }} The one-time token (if you need to build a custom URL)

The available variables differ slightly between templates — use the Preview panel to see exactly which variables are in scope for each template.


Branding your emails

To make emails feel like they come from your product rather than Anythink:

  • Set your organisation name in Settings → General — this populates {{ OrgName }} in all templates
  • If you have white-labelling enabled, your logo and brand colours are applied automatically to the email wrapper
  • Edit the body copy of each template to match your product's voice

Sender address

Emails are sent from Anythink's shared sending infrastructure. If you need emails to come from your own domain (e.g. noreply@yourapp.com), this is available on higher-tier plans — contact support to configure custom sending.

Email Templates | Automation | Anythink Docs | Anythink Docs