Spawn Workflow Steps
Spawn Workflow Steps
Section titled “Spawn Workflow Steps”The spawn step type is used for fan-out workflow execution. A parent workflow creates one child workflow instance per row in a line_items form field, optionally waits for their completion, then routes to the next step.
Typical uses: bulk reimbursement requests, multi-vendor invoice processing, onboarding task assignment, or any process where the number of sub-approvals is determined at submit time.
Basic Example
Section titled “Basic Example”spawn_individual_approvals: name: Process Individual Reimbursements type: spawn workflow: individual-reimbursement-approval # child workflow name items: reimbursement_items # line_items field on the parent form wait_for: all pass: - department - reimbursement_date map: employee_name: employee_name description: description amount: amount return: approved_amount: approved_amount # child field → parent item row field approver_name: approver_name on_complete: continue_to: completed on_failure: end_workflow: trueConfiguration Properties
Section titled “Configuration Properties”| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| workflow | String | Yes | Name of the child workflow to spawn (looked up by name in the same company). |
| items | String | Yes | Name of the line_items form field whose rows drive the fan-out. One child is created per row. |
| wait_for | String | No | Fan-in strategy: all (default), any, or none. |
| pass | List | No | Top-level parent form fields to copy verbatim into every child. |
| map | Dict | No | Explicit field renames: child_field: source_field. Source is resolved from parent top-level first, then from the current item row. |
| return | Dict | No | Values copied back from each completed child into the parent item row: child_field: row_field. |
| on_complete | Dict | No | Routing when all (or any) children finish successfully. |
| on_failure | Dict | No | Routing when a child is rejected. |
Fan-in Strategies (wait_for)
Section titled “Fan-in Strategies (wait_for)”| Value | Behaviour |
| :--- | :--- |
| all (default) | Parent pauses until every child instance completes. |
| any | Parent advances as soon as the first child completes. |
| none | Fire-and-forget — parent advances immediately after spawning. Children continue independently. |
How Child Data Is Built
Section titled “How Child Data Is Built”Each child's form data is assembled from three sources in priority order. A field set by an earlier source is never overwritten by a later one.
| Priority | Source | Description |
| :--- | :--- | :--- |
| 1 (highest) | map: entries | Explicit renames. Source is resolved from the parent's top-level request_data first; if not found there, from the current item row. |
| 2 | pass: fields | Copies parent top-level fields by the same name into the child. Skipped if the key was already set by map:. |
| 3 (lowest) | Item row keys | Every key in the current line item row is merged in, filling any slots not already set by map: or pass:. |
Example: Bulk Reimbursement
Section titled “Example: Bulk Reimbursement”Parent form fields:
department(text) — global contextreimbursement_date(date) — global contextreimbursement_items(line_items) — rows withemployee_name,description,amount,category
Spawn step:
map: employee_name: employee_name # explicit (identical source/dest — optional) description: description amount: amountpass: - department - reimbursement_dateChild instance for row {employee_name: "Alice", description: "Hotel", amount: 250, category: "accommodation"}:
{ "employee_name": "Alice", "description": "Hotel", "amount": 250, "department": "Engineering", "reimbursement_date": "2026-06-01", "category": "accommodation"}category arrives via step-3 auto-merge even though it is not listed in map: or pass:.
Parent Form: line_items Field
Section titled “Parent Form: line_items Field”The items: property must reference a line_items field on the parent form. Each row in that field becomes one child instance.
form: fields: - name: reimbursement_items type: line_items label: Reimbursement Line Items required: true min_items: 1 item_fields: - name: employee_name type: text label: Employee Name required: true - name: amount type: currency label: Amount required: true currency: USD validation: min: 0.01 max: 100000 - name: category type: dropdown label: Category required: true options: - value: travel label: Travel - value: meals label: MealsSee Field Types Reference for the full line_items schema.
Child Workflow Auto-Scaffolding
Section titled “Child Workflow Auto-Scaffolding”If the child workflow named in workflow: does not exist, the Validate button in the Workflow Designer shows an error with a Generate & Open Child Workflow button.
Clicking it creates a new child workflow pre-populated with:
- All fields from
map:targets — with correcttype,required,currency, andvalidationcarried over from the parent'sitem_fields - All fields from
pass:— with their full definitions copied from the parent form - Any remaining
item_fieldsnot covered bymap:orpass:, also with full definitions
This means a currency field with validation: {min: 0.01, max: 100000} will appear as a currency field (not text) in the generated child workflow.
Coordinator Step
Section titled “Coordinator Step”While children are running, the parent instance holds a system:spawn_coordinator step in PENDING state. This step stores:
items_field— theline_itemsfield nameworkflow_name— the child workflowwait_for— the configured fan-in strategyreturn— optional child-to-parent field mappingon_complete/on_failure— routing for when the children resolve
When the coordinator is resolved to APPROVED, any return mapping is applied first: child field values are written back into the corresponding rows of the parent's items field. The parent workflow then advances.
Returning Data from Children
Section titled “Returning Data from Children”Use the return block to copy values produced by each child workflow back into the parent row that spawned it.
return: approved_total: total_approved # child.request_data.approved_total → row.total_approved approver_name: approved_by # child.request_data.approver_name → row.approved_byRules:
returnis applied only on a successful fan-in (on_complete). It is ignored when children fail.- The left-hand key is a field name in the child workflow's
request_data. - The right-hand value is a field name in the parent item row.
- Only rows with a completed child are updated. With
wait_for: any, rows whose children did not finish are left unchanged. - Missing or
nullchild values do not overwrite existing row values.
This is useful for aggregating per-child outcomes (e.g. the approved amount, assigned approver, or final status) back into the parent's line items for reporting or downstream steps.
Instance Visibility
Section titled “Instance Visibility”Users who can view the parent workflow instance automatically have visibility into all its child instances. This applies to:
- The employee who submitted the parent request
- Managers (by org hierarchy) of the submitter
- Approvers who acted on any parent step
- Anyone with a
view_all_rolescompany role on the parent workflow
This means a finance manager who approved the parent bulk-reimbursement request can open each individual child reimbursement approval without needing a separate permission grant.
Test Mode Behaviour
Section titled “Test Mode Behaviour”In test mode (is_test_mode: true in instance metadata), the spawn step logs what it would do — item count, field mappings — without creating real child instances. The coordinator step is created as APPROVED immediately, and the parent advances via on_complete routing. This lets you test parent-level logic without needing the child workflow to exist.
Edge Cases
Section titled “Edge Cases”| Scenario | Behaviour |
| :--- | :--- |
| items field is an empty list | Coordinator resolves immediately (APPROVED); zero children created; parent follows on_complete. |
| wait_for: none | All children are created, then the coordinator resolves immediately; parent does not wait. |
| Child workflow not found | Parent follows on_failure routing (or is rejected if on_failure is not set). |
| map: source key missing in both parent and item row | That child field is left unset (not set to null), allowing item_fields auto-merge to fill it. |