Async Handoffs
Use start_workflows() when a workflow needs to hand discovered work off to independent top-level workflows instead of keeping everything inside one run.
If you are explicitly modelling agent runs, start_agents() and wait_for_agents() are semantic aliases with agent-oriented defaults.
For larger end-to-end examples, see Agent Orchestration.
This is useful for agent-style orchestration:
- a planner workflow discovers tasks at runtime
- each task becomes its own durable workflow
- the parent stores those workflow IDs in state
- the parent can later wait for
all,any,quorum, or a named started group
The ->start_workflows() method
use Queuety\Enums\WaitMode;
use Queuety\Queuety;
$agent_task = Queuety::workflow( 'agent_task' )
->then( ResearchTopicStep::class )
->then( SummarizeTopicStep::class );
Queuety::workflow( 'brief_research' )
->then( PlanTopicsStep::class )
->start_workflows(
items_key: 'topics',
workflow_builder: $agent_task,
result_key: 'child_workflow_ids',
group_key: 'researchers',
)
->wait_for_workflow_group( 'researchers', WaitMode::All, result_key: 'child_results' )
->then( SynthesizeBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );In that example:
PlanTopicsStepwrites an array of topic payloads to$state['topics']start_workflows()dispatches oneagent_taskworkflow per topic- the started workflow IDs are written to
$state['child_workflow_ids'] wait_for_workflow_group()pauses until that started group satisfies the chosen completion conditionSynthesizeBriefStepreceives the completed child states under$state['child_results']
Child payloads
Each item in items_key becomes the initial payload for one started workflow.
If the item is an array, its public keys are merged into the child payload:
[
[ 'topic' => 'pricing', 'source' => 'competitors' ],
[ 'topic' => 'reviews', 'source' => 'customers' ],
]If the item is scalar, Queuety stores it under payload_key:
->start_workflows(
items_key: 'topic_names',
workflow_builder: $agent_task,
payload_key: 'topic',
)Inheriting parent state
By default, start_workflows() merges the parent workflow's public state into every child payload.
That means shared context such as brief_id, account_id, or campaign is automatically available in each started workflow.
The source items_key and the result_key are excluded from inheritance so each child does not receive the whole task list or the final workflow ID array.
Set inherit_state: false when each child should receive only its item payload.
Why not run_workflow()?
run_workflow() creates a parent-child relationship where the parent parks on that step until the child completes.
start_workflows() is looser:
- the started workflows are independent top-level workflows
- each started workflow can be inspected, retried, or exported on its own
- the parent decides later whether and how to wait on them
That makes start_workflows() a better fit for async agent task handoff, while run_workflow() remains the better fit for nested composition inside one orchestration tree.
Waiting later
start_workflows() is designed to pair with Workflow Dependencies:
use Queuety\Enums\WaitMode;
Queuety::workflow( 'review_board' )
->start_workflows( 'review_tasks', $review_task_workflow, 'review_workflow_ids' )
->wait_for_workflows( 'review_workflow_ids', WaitMode::Any, 'winner' )
->then( FinalizeDecisionStep::class )
->dispatch( $payload );WaitMode::Allcollects all completed child statesWaitMode::Anyresumes on the first successful child workflow
This gives you planner/executor orchestration without needing a full runtime-editable DAG engine.
Agent-friendly aliases
start_agents() is an alias for start_workflows() with defaults tuned for planner/executor flows:
result_keydefaults toagent_workflow_idspayload_keydefaults toagent_tasknamedefaults tostart_agents
wait_for_agents() is an alias for wait_for_workflows() with defaults tuned for collecting agent results:
workflowsdefaults toagent_workflow_idsresult_keydefaults toagent_resultsnamedefaults towait_for_agents
wait_for_agent_group() is an alias for wait_for_workflow_group() with defaults tuned for agent batches.
use Queuety\Enums\WaitMode;
$agent_run = Queuety::workflow( 'agent_run' )
->then( ResearchTopicStep::class )
->then( SummarizeTopicStep::class );
Queuety::workflow( 'brief_research' )
->then( PlanTopicsStep::class )
->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2 )
->then( SynthesizeBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );