Debugging and logs
Find out why a workflow did or didn't run. Covers live logs in the builder, failed-run records from the processes API, reading their context, the most common error messages, and a checklist for workflows that silently do nothing.
A workflow that misbehaves either fails (a step throws, and Kustomer records the error) or does nothing (no error, but no effect either). Failures are easy to find. Doing nothing usually means a condition didn't match, and the logs show you which one. For how a run moves from step to step, see How workflows run.
Watch runs live
Open the logger
Open the workflow and select View Logs to open Workflow Log Events. Turn on Debug Mode. Kustomer records the workflow's runs for the next hour, which sets
logging: trueon the workflow.Cause the trigger
Do the thing that should start the workflow, such as updating a test conversation. Remember that a workflow you just saved can take about a minute to start matching.
Refresh and inspect
Refresh the logger. Each run is listed as a log event with when it completed, how long it took and its last step. Open a run to see every logged step, then select a step to see its input and output. If nothing ran, the logger shows "No applicable logs".
A step's output in the logger is what /#steps.<id> resolves to in later steps. If a param is coming out empty, compare its path with the output of the step it references. A misspelled field or the wrong step ID is the most common cause. Remember that the numbers on the canvas are positions, not step IDs.
Each logged run records, for every step, its input, its output, whether it succeeded and when it ran, plus the workflow version that ran. Secrets that Kustomer adds to a step's input are removed from the log. A param that's missing from a step's input usually means its reference resolved to null or undefined, so the param wasn't sent.
Read failed runs with the API
Every failed run is stored as a process. List a workflow's failures, newest first, optionally filtered by date:
kusty api GET /v1/workflows/$WORKFLOW_ID/processes --query createdAtFrom=2026-10-20T00:00:00Z --query pageSize=20 --output json{
"type": "workflowProcess",
"id": "5eea124bccb9de001933f317",
"attributes": {
"status": "failed",
"createdAt": "2026-10-21T12:53:30.684Z",
"error": "{\"message\":\"Invalid regular expression: /*/: Nothing to repeat\",\"step\":\"Se73DhAs5\"}",
"event": { "name": "kustomer.customer.update", "data": { "type": "customer", "id": "5e83c3a4d95b7e0012dc9711", "attributes": { … } } },
"meta": {
"context": "{\"order\":[\"1\",\"tNG_FZugW\",\"Se73DhAs5\"],\"steps\":{\"1\":{\"name\":\"kustomer.customer.update\",\"output\":{…}},…}}",
"attempt": 1,
"retry": false,
"workflowRev": 27
}
}
}errorstring (JSON)- The error as a JSON string:
message, andstep, the ID of the step that failed. eventobject- The raw event that started the run, before the trigger flattened it.
meta.contextstring (JSON)- The run's context as a JSON string.
orderlists the steps that ran;steps.<id>.outputis each one's output. Variables are removed. meta.workflowRevnumber- The workflow revision that ran. If it's older than the current one, you may have already fixed the problem.
statusstringfailed.
error and meta.context are strings, so parse them before reading. This script prints a one-line summary per failure:
const base = 'https://acme.api.kustomerapp.com'
const res = await fetch(`${base}/v1/workflows/${process.env.WORKFLOW_ID}/processes?pageSize=50`, {
headers: { Authorization: `Bearer ${process.env.KUSTOMER_API_KEY}` },
})
const { data } = await res.json()
for (const p of data) {
const error = JSON.parse(p.attributes.error ?? '{}')
const context = JSON.parse(p.attributes.meta?.context ?? '{}')
console.log(
p.attributes.createdAt,
`rev ${p.attributes.meta?.workflowRev}`,
`${p.attributes.event?.name} ${p.attributes.event?.data?.id}`,
`path ${(context.order ?? []).join(' → ')}`,
`failed at ${error.step}: ${error.message}`,
)
}Common errors
REST steps that get an error status from your service don't appear here: they succeed, and your transitions handle the status. See Handle responses and failures.
Errors when you save
Some problems are caught when you save rather than when the workflow runs. The most common is a loop: Step with ID <id> transitions to a previous step with ID <workflowId>:<target>, shown on the builder's canvas as "Cycle detected". Others include a duplicate name (409), nested root meta, and duplicate transitions. The full list is in Validate before you save.
When something runs twice
A run can repeat work for two reasons:
- A system retry. When a Kustomer action gets a
5xx, or the platform has a temporary problem, the run starts again from step1. Every step before the failure runs again, including creates, notes and REST calls. Make those steps safe to repeat: look up before you create, prefer Add Tags over replacing a tag list, and make your own endpoints idempotent. These retries don't appear in View errors. - The workflow triggered itself. An update trigger fires again for the workflow's own change. Guard it with a condition on
changes, as described in Workflows.
When nothing happens
Work down this list. Most "it didn't run" reports stop at one of the first four items.
- Enabled? Check the toggle or
enabledin the JSON. - Waited a minute? New and changed workflows take about 60 seconds to start matching.
- Right trigger? A new message fires
kustomer.message.createand alsokustomer.conversation.update. Make sure you picked the event that actually happens. - First condition matched? Open the run in View Logs. A run that stops after the trigger means no trigger transition matched. Look at step
1's output and compare it with the condition, paying attention to types ("4"vs4), IDs vs names (tags and users are IDs), andchangespaths. - Branch order? The first matching transition wins. A broad condition before a specific one swallows its runs. In the builder, that's the leftmost branch.
- Empty values? A param or condition that comes out empty often references a variable by name instead of ID (
{{{variables.apiKey}}}renders nothing), uses a step's position number instead of its ID, or reads callable inputs at/#steps.1.inputs.<name>instead of/#steps.1.<name>. - Saved, or only edited? Canvas changes don't apply until you Save, and Edit workflow JSON shows the last saved version. Changes then take about a minute to reach new runs.
- Replaced? Another enabled workflow with
replacesset to this workflow's ID stops it running. - Rate limited? Events over your org's workflow limit are skipped, not queued. Look for bursts, such as bulk imports.
- Did it run and then get undone? Another workflow or an integration may be overwriting the change. Check the record's timeline.
Test safely
- Gate on a test marker. While building, make the trigger's first condition require a tag such as
wf-test(contains [/#steps.1.tags, "<tagId>"]). Only your test conversations run the workflow. Remove the gate when you go live. - Use a sandbox org for anything that writes data, sends requests to partners or could loop.
- Watch for loops. A run count that climbs without new activity means the workflow is triggering itself. Disable it, then add a
changesguard as described in Workflows. - Version your JSON. Keep workflow JSON in source control so you can diff a revision that broke something against the last good one. Save it from
GET /v1/workflows/{id}rather than Export JSON, which leaves outresponse,enabledandlogging.meta.workflowRevon each failure tells you which revision ran, and the builder's version picker opens the 30 most recent versions read-only. - Add error cases in JSON. The classic builder has no error-case editor. Use Edit workflow JSON or the API, as described in Handle errors.