A job is a trigger, conditions and an ordered list of actions, set up under Customization › Orchestration in the admin; jobs were called hooks before the module was renamed, and the API's routes keep that name. What starts a job, what it can do, branches and loops, email, scheduled work, integrations with other systems, and every event and action.
L2–L3
Unit 02 · What starts a job
Chaining one job to another
What starts a jobPage 7 of 31Updated
A Trigger job action starts a second job and hands it data. How the second job runs depends on its own synchronous setting, what it receives, how a failure travels back or does not, and when two jobs are better than one.
Who this is for. Anyone whose job is growing, or who wants one piece of work shared by several jobs. Afterwards you can split a job in two and predict where a failure will show.
How the second job runs
The Trigger job action names a Target job and renders Event data, a template that produces JSON. How the target runs is decided by the target's own Synchronous execution setting, not the caller's:
Target
Runs
A failure in the target
Synchronous
At once, inside the caller's run, before the caller's next action
Fails the Trigger job action with Triggered hook exited with error status, and so fails the caller's run
Asynchronous
Queued as its own run; the caller carries on at once
Stays in the target's own run. The caller has already succeeded
Either way the target has its own entry in Batch Logs, with Job as its trigger. The Trigger job action leaves the target run's id as targetBatch in its result.
What the second job receives
On event
Returns
getData()
The event data, decoded from JSON into an array
getSourceHook(), getSourceBatch()
The job and the run that triggered this one
getSourceAction()
The Trigger job action itself
Event data that does not render to valid JSON is not an error: the target runs with no data and the caller's action succeeds. Build the data as a value and encode it, and have the target check for what it needs before it acts.
A two-job chain
A form job finds the member who sent an enquiry, then hands the rest to a second job that several other jobs also use. The first job's Trigger job action, Event data:
The second job, Job: after enquiry → email the account manager, reads it back:
Template code
{$handoff = $event->getData()}
Enquiry {$handoff.submissionId} is from member {$handoff.memberId}.
{% set handoff = event.getData() %}
Enquiry {{ handoff.submissionId }} is from member {{ handoff.memberId }}.
The depth is for the case where jobs can trigger each other: each passes depth + 1, and a condition at the top stops at a limit, so a mistake ends after a few runs rather than never.
Chaining instead of waiting for an event
A record written by an asynchronous job starts no other job (Changes that start nothing). If the next step should happen when a job creates a record, trigger the next job from the action after the create, with the new record's id (runData.get('create-it').id) in the event data, rather than a Created trigger that will never fire.
One job or two
Split a job when the second half:
is shared by several first halves, such as a single job that sends an order to the warehouse, started by both the payment event and a retry schedule;
needs a different Synchronous execution setting or a different Batch persistence;
should not take the first half down with it: an asynchronous target's failure stays in its own run.
Keep one job when the steps only make sense together and a reader needs to see them in one place. A chain is two run logs to read instead of one, and the link between them is a run id, not a line in the same list.