Trigger, Event & Scheduler Bindings
Connect pipelines to Salesforce record events, Platform Events, and cron schedules — all through declarative metadata. One line of trigger code, zero pipeline-specific Apex.
Record triggers — bind a pipeline to SObject events
First, write the trigger. This is the only trigger code you write — ever — for any FlowMason pipeline on this SObject:
// The only trigger code you'll ever write for FlowMason:
trigger AccountTrigger on Account (after insert, after update, after delete) {
FMTriggerFramework.dispatch();
}
// That's it. The binding in FM_Trigger_Binding__mdt takes it from here. Then create a binding record in FM_Trigger_Binding__mdt. You can do this through Setup → Custom Metadata Types, or deploy it as SFDX metadata:
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<label>Account AI Summary on Insert</label>
<values>
<field>SObject_API_Name__c</field>
<value xsi:type="xsd:string">Account</value>
</values>
<values>
<field>Trigger_Event__c</field>
<value xsi:type="xsd:string">after_insert,after_update</value>
</values>
<values>
<field>Pipeline_Id__c</field>
<value xsi:type="xsd:string">account-summarize-v1</value>
</values>
<values>
<field>Error_Mode__c</field>
<value xsi:type="xsd:string">continue</value>
</values>
<values>
<field>Execution_Mode__c</field>
<value xsi:type="xsd:string">async</value>
</values>
<values>
<field>Is_Active__c</field>
<value xsi:type="xsd:boolean">true</value>
</values>
</CustomMetadata> Binding fields reference
| Field | Required | Values |
|---|---|---|
SObject_API_Name__c | Yes | Account, Case, My_Object__c, etc. |
Trigger_Event__c | Yes | Comma-separated: after_insert, after_update, after_delete, before_insert, before_update |
Pipeline_Id__c | Yes | The pipeline's DeveloperName or custom object Id |
Execution_Mode__c | No | async (default) — enqueue Queueable per record; sync — run inline; bulk — single Queueable for the whole batch |
Error_Mode__c | No | continue (default) — log and skip; abort — throw and rollback; mark — call addError on the record |
Is_Active__c | Yes | true / false |
The trigger input shape
Inside your pipeline, the trigger provides a rich input context — no SOQL query needed to get the triggering record's fields:
// Inside your pipeline, the trigger provides this input shape:
// {{input.recordId}} — Id of the triggering record
// {{input.record.Name}} — field value from the triggering record
// {{input.record.Stage__c}} — any field on the new record state
// {{input.oldRecord.Stage__c}} — field value before the update (for update events)
// {{input.operation}} — "insert" | "update" | "delete"
// {{input.objectType}} — "Account" | "Case" | etc.
// Example pipeline stage config using trigger input:
// prompt: "Summarize this account: {{input.record.Name}} - {{input.record.Industry}}" Execution modes explained
- async (default): The framework enqueues one
PipelineQueueableper record. Each runs in its own transaction, with its own governor limits. This is the right choice for most AI workloads — callouts aren't allowed in the synchronous trigger context anyway. - sync: Runs the pipeline inline within the trigger transaction. Only viable for pipelines with no HTTP callouts (pure data transformation). Watch governor limits closely.
- bulk: Enqueues a single
PipelineQueueablefor the whole batch of records. Saves callout budget on bulk data loads. The pipeline receives{{input.records}}as a list.
Platform Events — react to published events
Connect a pipeline to any Platform Event using FM_Event_Binding__mdt and FMEventFramework.dispatch(). The pattern is identical to trigger bindings.
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<label>Order Placed to Fulfillment Pipeline</label>
<values>
<field>Event_API_Name__c</field>
<value xsi:type="xsd:string">OrderPlaced__e</value>
</values>
<values>
<field>Pipeline_Id__c</field>
<value xsi:type="xsd:string">order-fulfillment-v1</value>
</values>
<values>
<field>Execution_Mode__c</field>
<value xsi:type="xsd:string">async</value>
</values>
<values>
<field>Is_Active__c</field>
<value xsi:type="xsd:boolean">true</value>
</values>
</CustomMetadata> // One-liner trigger on the Platform Event:
trigger OrderPlacedTrigger on OrderPlaced__e (after insert) {
FMEventFramework.dispatch();
}
// Inside the pipeline, the event provides:
// {{input.event.OrderId__c}} — event field
// {{input.eventName}} — "OrderPlaced__e"
// {{input.replayId}} — platform event replay Id Scheduled runs — cron-driven pipelines
Schedule any pipeline to run on a cron expression. This is useful for nightly enrichment jobs, weekly report generation, or any periodic AI workload. The scheduler reads from FM_Schedule_Binding__mdt to resolve which pipeline to run at fire time.
// Schedule a pipeline to run on a cron expression:
FMScheduler.schedule('nightly-lead-enrichment', '0 0 2 * * ?');
// List what's scheduled:
List<FMScheduler.JobInfo> jobs = FMScheduler.listScheduled();
for (FMScheduler.JobInfo job : jobs) {
System.debug(job.jobName + ' — next fire: ' + job.nextFireTime);
}
// Stop a schedule:
FMScheduler.unschedule('nightly-lead-enrichment'); <?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<label>Nightly Lead Enrichment</label>
<values>
<field>Job_Name__c</field>
<value xsi:type="xsd:string">nightly-lead-enrichment</value>
</values>
<values>
<field>Pipeline_Id__c</field>
<value xsi:type="xsd:string">lead-enrich-v1</value>
</values>
<values>
<field>Input_JSON__c</field>
<value xsi:type="xsd:string">{"batchSize": 200, "segment": "new"}</value>
</values>
<values>
<field>Is_Active__c</field>
<value xsi:type="xsd:boolean">true</value>
</values>
</CustomMetadata> Error modes
When a pipeline fails inside a trigger, Error_Mode__c controls what happens next:
- continue (default): Log the error to
Pipeline_Stage_Log__cand move on to the next record. The triggering transaction succeeds. Good for non-critical enrichment (summarization, tagging). - abort: Rethrow the exception, rolling back the transaction. Use when the AI step is load-bearing for data integrity.
- mark: Call
addError()on the specific record that failed. Blocks only that record in the batch — others succeed. Useful when you need to surface the failure to the user in real time.
Troubleshooting
Pipeline isn't firing on record save
- Check that
Is_Active__cistrueon the binding record - Verify the
Trigger_Event__cvalue matches the actual operation (after_insert for new records, after_update for edits) - Confirm the trigger on the SObject calls
FMTriggerFramework.dispatch()
Can't see the pipeline execution in PipelineExecution__c
GovernorLimitException in sync execution mode
Execution_Mode__c to async.