Returning.AIDocs
v1

Guides / Workflow Builder

.md

Edit safely: revisions, conflicts, export, import and copying a workflow

Most workflow incidents are not logic bugs. They are a save that silently overwrote someone else's edit, a change that went live because nobody noticed the workflow was deployed, or a workflow copied to another community that still pointed at the first community's fields.

The API removes most of the guesswork: every write can be revision-checked, every response is the stored state, and a copy gets new identifiers. What it does not do is stop you: the checks are opt-in, so the routine below is what actually keeps a live workflow safe. This page is the safe editing routine, and it replaces the recovery workarounds that existed before the API.

What the API makes unnecessary

Old workaroundWhy it is no longer needed
Reading a save response and hoping it landedEvery write returns the stored object; a readback is one call
Manual re-read before an edit to dodge a raceSending If-Match refuses a stale write instead of overwriting it
Guessing whether a workflow is deployedstatus is in every read. A write to a deployed workflow is allowed and applies immediately, so read the status before you change a live one
Exporting to see whether a credential leaked into a fileThe export redacts credentials and lists where
Rebuilding a copied workflow by handPOST /{workflowId}/duplicate copies steps, trigger and references
Comparing screenshots to see what changedTwo reads compared by revision answer whether anything changed

The one thing the API does not replace is a person deciding whether a change is wanted.

1. Read before you write

GETGet a workflowcURL
curl --request GET \
  --url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b" \
  --header "Authorization: Bearer <API_KEY>"

Keep revision, status, the step ids and the options of the step you mean to change. Note which credentials appear in redactedPaths: those are the values you must echo back rather than retype.

2. Make the smallest change

PATCHUpdate a stepcURL
curl --request PATCH \
  --url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/nodes/65f1a2b3c4d5e6f708192a3d" \
  --header "Authorization: Bearer <API_KEY>" \
  --header "Content-Type: application/json" \
  --header "If-Match: 162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c" \
  --data '{"name":"Notify receiver (v2)"}'

PATCH on a step replaces options wholesale but leaves fields you omit alone, so send only what changes. The response is the stored step and the new revision.

3. Handle a conflict instead of retrying through it

A 409 VERSION_CONFLICT means someone else changed the workflow, usually a person in the canvas. meta.currentRevision is the live revision.

  1. Re-read with GET /{workflowId}.
  2. Compare the step you meant to change with what you sent.
  3. Rebuild the change on the new revision.
  4. Send it again with the new If-Match.

Do not resend the old body with the new revision without looking: that is how one editor's change is silently undone by another's.

Nothing refuses a write to a live workflow. The API does not ask whether you meant it: a change to a deployed workflow takes effect for the next run, so read status in step 1, and use the copy routine below when the change deserves a review.

4. The safe change to a live workflow

  1. POST /{workflowId}/duplicate to get a draft copy with new ids.
  2. PATCH and POST /{workflowId}/nodes on the copy until it is what you want.
  3. POST /{workflowId}/validate and POST /{workflowId}/test on the copy.
  4. POST /{workflowId}/deploy on the copy, then POST /{workflowId}/undeploy on the original.
  5. Keep the original as the rollback.

A duplicate copies the failure webhook URL and the credentials, so check both on the copy before deploying it: two live workflows reporting to one receiver, or sharing one webhook key, is usually not what you want. There is no rotation call: to give the copy its own key, set its trigger to another type and then webhook again, which mints a new one, and reconfigure the copy's sender.

5. Export before a risky change

GETExport a workflow definitioncURL
curl --request GET \
  --url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/export" \
  --header "Authorization: Bearer <API_KEY>" \
  --output workflow-backup.json

The document is the portable definition with every credential replaced by the marker. It is the rollback plan: if the change goes wrong, import it as a new workflow. An export is still sensitive, because a secret an author typed inside a raw body template is returned as authored.

6. Copy a workflow to another community

Export from the source community, then import into the target with a key from the target community.

POSTCreate a workflowcURL
curl --request POST \
  --url "https://api.returning.ai/v1/workflow-builder/data-workflows" \
  --header "Authorization: Bearer <API_KEY_OF_THE_TARGET_COMMUNITY>" \
  --header "Content-Type: application/json" \
  --data '{"name":"Tier sync (copy)","definition":{"kind":"returningai.workflow","formatVersion":1,"variant":"data_workflows","workflow":{"name":"Tier sync"},"trigger":null,"nodes":[]}}'

Before the import, edit the file:

  • Set the workflow name.
  • Replace every credential with the marker, or delete it: the import blanks node credentials and reports them in secretsToFill.
  • Blank every saved defaultValue in selector and value slots. An exported default can be a real customer identifier or a reward amount.
  • Check the destination community has every field and role the steps write, with the same key and type. A field key that exists in the source community may not exist in the target: read the target's GET /catalog and compare its platformActionFields with the keys in the export before importing.

After the import:

  1. The response lists secretsToFill. Supply each one with PATCH /{workflowId}/nodes/{nodeId}. The list is kept on the server and survives a PATCH, including a rename, and each path drops out as soon as it holds a real value, so filling one credential does not clear the reminder for the rest. A later read does not show the marker itself, and an export writes the still-empty paths as ["__REDACTED__"], which is how you see what is left.
  2. POST /{workflowId}/validate reports MISSING_SECRET for any that are still empty. That is a warning, not a refusal: the deploy goes ahead with the credential empty, so fill them first.
  3. PUT /{workflowId}/trigger to give the copy its own trigger. A webhook trigger created there mints its own key; a copy that kept the source's webhook trigger keeps the source's key until you set another type and back.
  4. Deploy only if the source was deployed, and only after a mode: "dry-run" test.
  5. Re-export the copy and compare it with the file you imported, ignoring identifiers and the new trigger key.

Import creates a new workflow with new identifiers and rewires the same references a duplicate does: reference objects, field values that name a step, router branch lists, and {{stepId.path}} templates. The pairing is by step identity rather than position, so an insert after the trigger or a step inside a router branch still binds to the step it meant, and a template that points at the source's trigger or steps lands on the imported ones. Validate the copy anyway, because a reference to something outside the document has nowhere to point. The two communities' workflows can still share an external destination: an HTTP step's URL survives the import, so check every outside call before deploying the copy.

7. Failure handling

What you seeWhat it meansWhat to do
409 VERSION_CONFLICTAnother writer got there firstRe-read, rebuild the change, resend.
A change landed on a live workflow that you meant to stageThe API does not gate edits to a deployed workflowUse the copy routine, and read status before every write.
400 REDACTED_VALUE_WITHOUT_STORED_SECRETA marker arrived where nothing is storedSend the real value.
An HTTP step now calls the wrong host, still carrying a stored credentialThe marker is restored by path, so the credential follows the URLRepoint the step and replace the credential rather than echoing the marker.
MISSING_SECRET after an importA credential the import blanked is still emptyPatch that step with a real value.
The copy behaves differently from the sourceA field, role or identifier the target community does not haveCompare the steps and the target's field keys.
The canvas editor shows unsaved changes on an API-built stepThe editor fills a default the API did not storeSave deliberately in the canvas, then re-read and compare.

8. Cleanup

Delete the copy you no longer need once the original is retired, and delete the source only after the copy has run in the target. A delete works on a deployed workflow too, so undeploy first if you want the runs to stop before the workflow goes.

What the API does not cover

  • Replacing a whole existing workflow with an import. Import creates a new workflow; the old one is deleted or undeployed separately.
  • Moving a step between branches in one call. That is a delete plus a create.
  • Reviewing a change before it reaches members. Nothing in the API holds a change back: a write to a deployed workflow applies at once, so a review has to be a duplicate and a deploy of the copy.
  • Deciding whether a change is wanted. A conflict exits so a person can look, but only if you sent If-Match.

Endpoints on this page