Workflow JSON

The complete workflow document schema, field by field, with limits and patterns. Covers root keys and flat meta, triggers (event and callable), steps, transitions and the save-time loop check, error cases, the replaces field for overriding app workflows, and creating and partially updating workflows through the API.

Everything you build in the workflow builder is stored as a JSON document. Reading and writing that JSON lets you review workflow changes in pull requests, copy workflows between a sandbox and production, generate workflows with code or an AI agent, and ship them inside apps.

This page is the field-by-field reference. For a step-by-step tutorial, see Write workflow JSON.

A complete workflow
{
  "name": "vip-first-reply",
  "description": "Raise priority and tag new conversations from gold-tier customers",
  "enabled": true,
  "meta": { "displayName": "VIP first reply" },
  "trigger": {
    "id": "1",
    "eventName": "kustomer.conversation.create",
    "transitions": [
      { "target": "customer", "condition": { "op": "true", "values": [true] } }
    ]
  },
  "steps": [
    {
      "id": "customer",
      "action": "kustomer.customer.find",
      "params": { "identifier": "/#steps.1.customer" },
      "meta": { "displayName": "Look up the customer" },
      "transitions": [
        {
          "target": "raise",
          "condition": { "op": "eq", "values": ["/#steps.customer.custom.tierStr", "gold"] },
          "meta": { "name": "Gold tier" }
        }
      ],
      "errorCases": []
    },
    {
      "id": "raise",
      "action": "kustomer.conversation.update",
      "params": { "id": "/#steps.1.id", "priority": 4 },
      "meta": { "displayName": "Raise priority" },
      "transitions": [
        { "target": "tag", "condition": { "op": "true", "values": [true] } }
      ]
    },
    {
      "id": "tag",
      "action": "kustomer.conversation.append-tags",
      "params": { "id": "/#steps.1.id", "tags": ["64f1c0ffee5ca1ab1e000002"] },
      "meta": { "displayName": "Tag VIP" },
      "transitions": []
    }
  ]
}

Root

namestringrequired
descriptionstring
enabledboolean
triggerobject
stepsobject[]
responseobject
objectMappingobject
loggingboolean
replacesstring | null
metaobject

No other root keys are allowed. Unknown keys are rejected.

Root meta must be flat:

RuleLimit
KeysMatch ^[a-zA-Z][a-zA-Z0-9-_]{1,64}$: a letter, then letters, numbers, - or _
ValuesStrings of up to 1,024 characters, numbers, booleans or null
SizeUp to 100 keys
NestingNot allowed. An object or array value is rejected

Trigger

Event trigger

JSON
{ "id": "1", "eventName": "kustomer.conversation.update", "transitions": [ … ] }
idstringrequired
eventNamestringrequired
transitionsobject[]required
appVersionstring
metaobject

Callable trigger

JSON
{ "id": "1", "callable": true, "schema": { "type": "object", "properties": { … } }, "transitions": [ … ] }
idstringrequired
callabletruerequired
schemaobjectrequired
transitionsobject[]required

All four keys are required; a callable trigger without one is rejected when you save. Declare the workflow's outputs in the root response key.

How callers run callable workflows is covered in Callable and scheduled workflows.

Steps

idstringrequired
actionstring
paramsobject
transitionsobject[]required
errorCasesobject[]
appVersionstring
metaobject

Only id and transitions are required. The numbers on the builder's canvas (1, 2, 3…) show each step's position, not its ID. Only the trigger's ID is always 1; use Copy Step ID in the builder to get any other step's ID.

Transitions

JSON
{ "target": "raise", "condition": { "op": "eq", "values": ["/#steps.customer.custom.tierStr", "gold"] }, "meta": { "name": "Gold tier" } }
targetstringrequired
conditionobjectrequired
metaobject

The first transition whose condition passes wins. If none pass, the run ends successfully. In the builder, a step's branches sit side by side, and their left-to-right order is the order of transitions.

Kustomer checks the transitions when you save:

  • No loops. A transition can't target a step that's already on the path leading to it. Kustomer rejects the save with 400: Step with ID <id> transitions to a previous step with ID <workflowId>:<target>. The builder shows the same problem on the canvas as "Cycle detected". To repeat work later, schedule a callable workflow.
  • Unique transitions. Two identical transitions on one step are rejected.
  • At most 10 transitions per step.

To keep a workflow readable in the builder, give each branch its own final step rather than pointing two branches at the same step. Kustomer's own templates do this.

Error cases

JSON
{
  "condition": { "op": "and", "values": [{ "op": "eq", "values": ["/#error.status", 400] }] },
  "errorAction": { "type": "continue" }
}
conditionobjectrequired
errorAction.typestringrequired

Cases are checked in order, and the first match applies. A step can have up to 10.

Read-only attributes

The API returns these alongside your fields. Don't send them back.

AttributeMeaning
lastErrorThe most recent failure, or {}
editableWhether the workflow can be edited in the builder
containsVariablesWhether the trigger or steps reference a variable (/#variables. or {{variables.). Variables are loaded for a run only when it's true
createdAt, updatedAtTimestamps

Create and update with the API

  1. Create

    Shell
    curl -X POST "https://acme.api.kustomerapp.com/v1/workflows" \
      -H "Authorization: Bearer $KUSTOMER_API_KEY" -H "Content-Type: application/json" \
      --data @vip-first-reply.json

    The response is the workflow with its id. Create it with "enabled": false, add any variables it needs, then reference them by the IDs the variables endpoint returns, and enable it.

  2. Update

    PUT /v1/workflows/{id} updates only the root keys you send. Each key you send replaces the stored value wholesale (the whole steps array, for example), and keys you leave out are unchanged. Always send trigger and steps together, because transitions and steps refer to each other.

    Shell
    kusty api GET /v1/workflows/$ID --output json | jq '.data.attributes | {trigger, steps} | del(.steps[]._id)' > wf.json
    # edit wf.json
    kusty api PUT /v1/workflows/$ID --data @wf.json --dry-run
    kusty api PUT /v1/workflows/$ID --data @wf.json

    For a callable workflow, include response too if its outputs change. To switch a workflow on or off, send only enabled, as the builder's switch does:

    Shell
    kusty api PUT /v1/workflows/$ID --data '{ "enabled": true }'

    Each save creates a new version, and Kustomer keeps the 30 most recent. Workflows installed by an app can't be edited (409), but you can still turn them on or off and change logging.

  3. Wait, then test

    Allow about a minute for the change to apply, then cause the trigger event and check the logs.

Edit workflow JSON, Export JSON and Duplicate

The builder has three ways to get at the JSON. They don't all include every root key:

FeatureWhat it covers
Edit workflow JSON (builder options menu)Shows description, name, steps and trigger from the last saved version, so save canvas edits first. Update saves the JSON straight to the workflow, and the server validates it
Export JSON (workflows list)Downloads description, name, meta, steps and trigger, with name changed to export-<name>--<uuid>
Duplicate (workflows list)Creates a copy with the same keys, named copy-<name>

Export JSON and Duplicate leave out response (a callable workflow's outputs), enabled and logging, so copies start disabled and callable copies need their Output Parameters added again. Both also remove trigger.eventName from callable workflows; Kustomer sets a new one when the copy is saved. To keep a complete copy, use GET /v1/workflows/{id} instead.

Override an app workflow with replaces

To change a workflow an app installed without editing the app's version, copy it, edit the copy, and set the copy's replaces to the original's ID. When both match an event, only the copy runs; the original is skipped.

JSON
{
  "name": "shopify-order-sync-custom",
  "replaces": "6513a0d1a4b1e1001a6b3e77",
  "enabled": true,
  "trigger": { … },
  "steps": [ … ]
}

Disable or delete the copy to restore the original behavior. Keep appVersion on copied steps that use the app's actions.

Validate before you save

The API validates documents against the schema and rejects invalid ones with a 400 validation error. Common causes:

Error mentionsFix
name patternUse only letters, numbers, -, _ and .; no spaces
409 on create or updateAnother workflow in the org already has this name (names are compared lowercased), or the workflow was installed by an app and can't be edited
additionalPropertiesRemove keys the schema doesn't allow, such as read-only attributes copied from a GET
metaRoot meta must be flat: no nested objects, keys that start with a letter, string values of up to 1,024 characters, and at most 100 keys
target patternStep IDs used as targets can't contain dots
maxItemsMore than 10 transitions or error cases on one step, or more than 5 conditions in an error case. Split the logic
uniqueItemsTwo identical transitions on one step. Remove the duplicate
transitions to a previous stepA transition loops back to a step already on its path. Restructure, or schedule a callable workflow to repeat work
Invalid app settings configuration passedA {{{ setting … }}} reference doesn't match the app's settings
appVersion patternUse <app>-<semver range>, such as kustomer-^1.8.16

In the builder, the on/off switch also refuses to enable a workflow while any condition lacks a target: "All conditions in active workflows must point to a target". Saving an enabled workflow runs the same check.