Reusable workflows
You can reuse logic across workflows by triggering a child workflow from a parent workflow through Port's API. The child reports its result back to the parent run when it finishes, letting you share standard sequences (such as approvals or provisioning flows) across many workflows.
When to use this pattern
- Multiple workflows share the same sequence of steps (for example, an approval or provisioning flow).
- You want a single place to update shared logic instead of duplicating nodes.
- The parent needs to continue after a long-running child via an asynchronous webhook.
How it works
- The parent workflow calls a webhook node with
synchronized: falsethat triggers a workflow run on the child. It passes its own node run id ({{ .workflowNodeRun.identifier }}) in the payload. - The child workflow runs the shared logic.
- When the child finishes, its final webhook node sends a
PATCHto update the parent's node run using that id, marking it complete.
The node run id is unique per execution, so the child always reports back to the correct parent run.
{{ .workflowNodeRun.identifier }} is the id of the current node run (format wfnr_xxxx), available in webhook and other action configs. See workflow run context for details.
1. Create the child workflow
- Add a self-service trigger with a string user input that carries the parent's node run id (for example
parent_node_run_id). Add any other inputs the shared logic needs. - Build the shared steps (webhooks, integrations, conditions, and so on).
- Add a final webhook node that completes the parent's node run:
{
"identifier": "report_to_parent",
"title": "Report result to parent",
"config": {
"type": "WEBHOOK",
"method": "PATCH",
"url": "https://api.port.io/v1/workflows/nodes/runs/{{ .outputs[\"trigger\"].parent_node_run_id }}",
"body": {
"status": "COMPLETED",
"result": "SUCCESS",
"output": {
"message": "Child workflow finished"
}
}
}
}
Replace trigger with your trigger node's identifier if it differs, and adjust output to expose any fields the parent should read.
If the child fails, send the same PATCH with "result": "FAILED" so the parent does not stay stuck in progress. Model this with a condition branch or a dedicated failure path using the onFailure configuratiotn.
2. Call the child from the parent
Add a webhook node that triggers the child asynchronously:
{
"identifier": "invoke_shared_logic",
"title": "Invoke shared workflow",
"config": {
"type": "WEBHOOK",
"method": "POST",
"synchronized": false,
"url": "https://api.port.io/v1/workflows/my_shared_workflow/runs",
"body": {
"inputs": {
"parent_node_run_id": "{{ .workflowNodeRun.identifier }}",
"example_arg": "{{ .outputs[\"trigger\"].someField }}"
}
}
}
}
- Replace
my_shared_workflowwith the child workflow's identifier. - Match each key under
inputsto a user input on the child trigger. - Keep
synchronizedset tofalseso the parent does not block. The child completes this node by calling the API.