Skip to content

A workflow starts a sync job for you when something happens in your environment, such as an account finishing its connection. Without one, your app has to catch the integrated_account:active webhook and call POST /sync-job-run itself.

Skip the reading — fast path for coding agents
CLI
truto workflows list -o json
Skill
View truto-jsonata skill on GitHub Cursor / Claude Code: /skill truto-jsonata

Workflows have no screen in the dashboard. Create and manage them with the API, the CLI or an SDK.

How a workflow runs

A workflow has a trigger_name and a config:

  • When the trigger fires in the workflow's environment, Truto evaluates the workflow's run_if. If it's falsy, nothing else happens.
  • Otherwise Truto creates a workflow run and runs the steps in order, one at a time. A step with its own run_if that evaluates falsy is skipped.
  • The run ends completed, or failed if any step failed. Its result lists each step with success, and error or result.

Every workflow in the environment with the same trigger runs, each with its own run.

Triggers

trigger_name Fires when
integrated_account:active An account finishes connecting through a link or the connect flow: its post-install and validation actions passed and it has no post-connect form left to fill in. It fires again when an existing account reconnects.
unified_webhook:batch Truto processed a batch of inbound vendor webhook events for an account into record:* events. See Receiving vendor webhooks.

The workflow's run_if and its steps see integrated_account_id, environment_id, integration (the account's integration, with integration.name), context (the account's context) and tenant_id. Steps also see payload, the trigger's data: for unified_webhook:batch that's integrated_account_id, events and batch_size.

Creating a workflow with any other trigger_name returns 404.

Steps

config.steps is an array. Each step has type: "run" and action: "run_sync_job", which is the one action there is.

Field What it does
config The body of the sync job run to create, as an object with {{placeholders}} or a JSONata string that returns it. It must include sync_job_id. The rest is what POST /sync-job-run takes: integrated_account_id and a webhook_id or datastore_id for runtime versions 1 to 3, or args for version 4.
cron_expression Instead of running the sync job once, create a schedule for it with this cron expression. The step's config becomes the schedule's body.
run_if A JSONata expression. The step runs only when it's truthy.

A run created by a step sends sync_job_run:created to your webhooks, like any other run.

Run a sync job when an account connects

This workflow runs a V4 sync job for every HubSpot or Salesforce account that connects, and sends the output to one webhook.

truto workflows create -b '{
  "trigger_name": "integrated_account:active",
  "config": {
    "run_if": "integration.name in [\"hubspot\", \"salesforce\"]",
    "steps": [
      {
        "type": "run",
        "action": "run_sync_job",
        "config": "{ \"sync_job_id\": \"<sync_job_id>\", \"args\": { \"integrated_account_id\": integrated_account_id, \"webhook_id\": \"<webhook_id>\" } }"
      }
    ]
  }
}'
curl -X POST "https://api.truto.one/workflow" \
  -H "Authorization: Bearer <API token>" \
  -H "Content-Type: application/json" \
  -d '{
    "trigger_name": "integrated_account:active",
    "config": {
      "run_if": "integration.name in [\"hubspot\", \"salesforce\"]",
      "steps": [
        {
          "type": "run",
          "action": "run_sync_job",
          "config": "{ \"sync_job_id\": \"<sync_job_id>\", \"args\": { \"integrated_account_id\": integrated_account_id, \"webhook_id\": \"<webhook_id>\" } }"
        }
      ]
    }
  }'

The step's config is a JSONata string, so integrated_account_id is read from the trigger. To pick a different sync job per integration, map on integration.name:

(
  $sync_job_id := $lookup({
    "hubspot": "<hubspot_sync_job_id>",
    "salesforce": "<salesforce_sync_job_id>"
  }, integration.name);
  {
    "sync_job_id": $sync_job_id,
    "integrated_account_id": integrated_account_id,
    "webhook_id": "<webhook_id>"
  }
)

To keep the account in sync after the first run, add "cron_expression": "0 */6 * * *" to the step. The workflow then creates a schedule for the account instead of a single run. Schedules run in UTC, at most every five minutes.

Check what a workflow did

GET /workflow-run?workflow_id=<id> lists a workflow's runs. Filter by status: created, running, completed or failed. A run's result.steps says which steps ran, which were skipped, and why a step failed.

truto workflow-runs list --workflow_id <workflow_id> --status failed
curl "https://api.truto.one/workflow-run?workflow_id=<workflow_id>&status=failed" \
  -H "Authorization: Bearer <API token>"

If a run fails, Truto retries it, and a retry runs every step again. A step that already created a sync job run can create another, so keep steps that can't run twice out of workflows whose other steps may fail. You can't start a workflow run yourself: only its trigger does.

Next

The full set of endpoints is in the Workflows and Workflow runs API references. For the sync job the step starts, see the V4 sync job reference.