Create a workflow step
/workflow-stepsAdds a step to a workflow and returns it with a Location. It goes at the end of the chain, or between after_step_ids and before_step_ids, moving later steps up; steps that don't link that way return 409. A floating step isn't linked. A kind: branched step needs branches, which rejoin the step that followed. A taken label or an unknown workflow_id returns 422. Every step but a branched one gets an optional effective_date field. It emits no webhook, and DELETE /workflow-steps/{id} undoes it.
Query parameters
Validate and run the call without committing it, returning what the real call would return with
meta.dry_runset.
Headers
Makes a retry safe. Send a new UUID for each new request and reuse it only to retry that request; the first response is replayed for 24 hours.
Request body
application/json, required
The workflow to add the step to.
- One of:
linear for an ordinary step, branched for a step that splits into branches.
linear,branchedDefault:"linear" The name people see.
The machine name. Defaults to custom_ and the label with spaces as underscores and other non-word characters removed, as the app names steps.
- One of:
What the step does: default (the default) or kyc. Not allowed on a branched step, which takes branch_split.
default,kyc What the step is for.
- Default:
Whether the step is protected from deletion.
false - Default:
Whether the step sits outside the main chain. Not allowed on a branched step.
false The steps the new step follows: one, or each parallel branch it rejoins. Without before_step_ids they must end the chain. Not allowed on a floating step.
The steps the new step leads to. Alone, the new step goes first. Not allowed on a floating step.
- One of:
How an investor on a branched step moves on: selection needs a required progress_to_step_id naming the branch, classification picks the first branch whose classifications the investor holds. Only on a branched step.
selection,classificationDefault:"selection" - Default:
Whether only internal users can move investors onto the step.
false Whether an internal-only step tells investors that their transaction is being processed internally. Only on an internal-only step (422 internal_only).
Asks the investor to upload a document to enter the step. Null removes it.
Holds investors to the deal's minimum investment. Null removes it.
Releases documents in the step's deal folders, creating them for the workflow's deals and renaming existing ones; a name a sibling folder has returns 409. Null removes it and keeps the folders.
Sends a webhook. Null removes it.
Whether the step posts the investment to Portfolio, with each deal naming the asset.
The branches of a branched step, in order. Required for kind branched and not allowed otherwise.
{
"workflow_id": "1b2c3d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"label": "Committed",
"is_protected": true
}Responses
{
"data": {
"id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"workflow_id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"name": "string",
"label": "string",
"type": "string",
"kind": "linear",
"description": "string",
"is_protected": true,
"is_floating": true,
"order": 1,
"has_custom_form": true,
"is_internal_only": true,
"show_default_internal_message": true,
"document_upload": {
"optional": true,
"document_name": "string",
"document_instruction": "string"
},
"minimum_investment": {
"optional": true,
"disclaimer": "string"
},
"document_release": {
"folder_name": "string"
},
"webhook": {
"webhook_id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"leading_edge": true
},
"posts_to_portfolio": true,
"info_banner": {
"title": "string",
"body": "string",
"delegated_to_deal": true
},
"requirements": [
{
"id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"class_name": "string",
"leading_edge": true,
"optional": true,
"delegated_to_deal": true,
"config": {}
}
],
"triggers": [
{
"id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"class_name": "string",
"leading_edge": true,
"delegated_to_deal": true,
"config": {}
}
],
"created_at": "2026-06-12T10:00:00+00:00",
"updated_at": "2026-06-12T10:00:00+00:00"
}
}Authorization failed. error.code is one of org_disabled,
ip_blocked, missing_scope, scope_disabled, user_disabled.
The request conflicts with existing state (error.code = conflict),
e.g. a compliance flow with the requested name already exists.
The request can't be processed (HTTP 422): validation_failed with one ErrorDetail per
problem, or a code documented on the operation.
Examples
curl -X POST https://your-org-domain/kong/core/api/v2/workflow-steps\
-H "Authorization: Bearer $HELM_API_KEY"\
-H "Content-Type: application/json"\
-d '{
"workflow_id": "1b2c3d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"label": "Committed",
"is_protected": true
}'