Use Mambo Merge from APEX

2.2.4 | 1/29/2024

Calling from Apex | Examples | Using the Invocable Method | Bulk and Governor Considerations | Error Handling Summary

Mambo Merge: startMerge Usage Guide

MamboMerge.startMerge is the entry point for running a merge programmatically. It accepts a set of input parameters, validates them, and queues a batch job that performs the merge. The same logic is exposed two ways: a flexible Apex method that takes a parameter map, and an invocable wrapper for use in Flows.


Overview

Both entry points ultimately do the same thing: resolve a Routine (or build an ad-hoc one from a file), validate the inputs against that Routine’s configuration, and queue a batch job. The merge runs asynchronously. The return value tells you whether the job was queued successfully — not whether the merge itself completed. Final merge results are written to Custom Metadata and can be looked up later by job ID.

A few concepts that apply to both entry points:

  • Routine vs. no Routine. If you pass a routineKey, the merge runs according to that Routine’s saved configuration (which records to pull, which template, where to save output, etc.). If you omit routineKey, you must pass a fileId and a recordId, and a minimal one-off merge is performed against that file and record.
  • Routines must be enabled for Apex. A Routine will only run through startMerge if its mergeFromApex flag is set. Otherwise initialization fails with “Routine not enabled for Mambo Merge from Apex”.
  • Asynchronous execution. Each successful call queues one batch job via Database.executeBatch. Salesforce limits the number of batch jobs you can queue per transaction, so be deliberate about how many merges you kick off at once.
  • Edition and license requirements. The edition of Mambo Merge must be on Performance Edition (or higher), the running user must be licensed for Mambo Merge, and there must be an active subscription or trial. These checks are skipped in test context.

Calling from Apex

Signature

global static Map<String, Object> startMerge(Map<String, Object> params)

Input parameters

The params map supports the following keys. All keys are optional at the map level — which ones are required depends on the Routine being run.

Key Type Description
routineKey String The Key of the Routine to run. Required if using a Routine.
templateNumber Integer The 1-based index of the template within the Routine. Required for single-template (non-checkbox) Routines.
fileId Id ContentDocument or ContentVersion ID of the file to merge. Required when not using a Routine, or for Routines configured to accept a file passed in (uploadToMerge).
recordId Id A single record to merge. Required for Single Record Routines and when not using a Routine.
recordIds List<Id> A list of record IDs. Required for Multiple Records Routines.
mergeData List<sObject>, List<Map<String, Object>>, or String Records to merge, overriding any data the Routine would otherwise query. Required for Routines configured to use passed-in merge data. A String value is deserialized as JSON representing a list of records.

Return value

Returns a Map<String, Object>. On success it contains:

Key Type Description
jobId Id The ID of the queued batch job. Use this to look up final results in Custom Metadata.
batches Integer The number of batches the merge was split into.
batchSize Integer The batch size used.
recordsCount Integer The number of records to be merged.

If the job could not be queued, the map instead contains:

Key Type Description
batchableError Map<String, String> Details about the exception that prevented queuing. Inspect the message (and type) keys for specifics.

Because errors are returned in the result map rather than thrown, always check for the batchableError key rather than relying on a try/catch around the call.

Examples

Running a Routine against a single record

Map<String, Object> result = MamboMerge.startMerge(
  new Map<String, Object>{
    'routineKey' => 'Account_Welcome_Letter',
    'templateNumber' => 1,
    'recordId' => acct.Id
  }
);

if (result.containsKey('batchableError')) {
  Map<String, String> err = (Map<String, String>) result.get('batchableError');
  System.debug('Merge could not be queued: ' + err.get('message'));
} else {
  System.debug('Queued job: ' + result.get('jobId'));
}

Running a Routine against multiple records

Map<String, Object> result = MamboMerge.startMerge(
  new Map<String, Object>{
    'routineKey' => 'Bulk_Statements',
    'templateNumber' => 1,
    'recordIds' => new List<Id>{ id1, id2, id3 }
  }
);

A one-off merge without a Routine

When no routineKey is provided, supply both a fileId and a recordId. The template number defaults to 1 and an ad-hoc Routine is built around the file.

Map<String, Object> result = MamboMerge.startMerge(
  new Map<String, Object>{
    'fileId' => templateContentVersionId,
    'recordId' => acct.Id
  }
);

Supplying merge data directly

For Routines configured to use passed-in merge data, provide it as a list of sObjects, a list of maps, or a JSON string.

// As sObjects
List<Account> accounts = [SELECT Id, Name, Industry FROM Account LIMIT 5];
MamboMerge.startMerge(
  new Map<String, Object>{
    'routineKey' => 'Custom_Data_Merge',
    'templateNumber' => 1,
    'recordId' => parentId,
    'mergeData' => accounts
  }
);

// As maps (useful for synthetic data that isn't a real record)
List<Map<String, Object>> rows = new List<Map<String, Object>>{
  new Map<String, Object>{ 'name' => 'Line 1', 'amount' => 100 },
  new Map<String, Object>{ 'name' => 'Line 2', 'amount' => 250 }
};
MamboMerge.startMerge(
  new Map<String, Object>{
    'routineKey' => 'Custom_Data_Merge',
    'templateNumber' => 1,
    'recordId' => parentId,
    'mergeData' => rows
  }
);

Using the Invocable Method

startMergeInvocable wraps startMerge for use in Flows. It accepts a list of structured request objects and returns a list of structured response objects, one per request, in the same order.

Method

@InvocableMethod(label='Mambo Merge: Start Merge' category='Mambo Merge')
global static List<MergeResponse> startMergeInvocable(List<MergeRequest> requests)

Request inputs (MergeRequest)

Variable Type Description
Routine Key String The Key of the Routine to run. Required if using a Routine.
Template Number Integer 1-based index of the template within the Routine. Required for non-checkbox Routines.
File ID Id ContentDocument or ContentVersion ID. Required when not using a Routine or when the Routine expects a file passed in.
Record ID Id Single record to merge. Required for Single Record Routines or when not using a Routine.
Record IDs List<Id> List of record IDs. Required for Multiple Records Routines.
Merge Data (Single Record) sObject A single record used as merge data.
Merge Data (List of Records) List<sObject> A list of records used as merge data.
Merge Data (JSON) String A JSON-serialized list of merge data records.

Merge data precedence

The three merge-data inputs are mutually exclusive and evaluated in priority order. Only the first one provided is used:

  1. Merge Data (Single Record) — if set, it’s wrapped into a one-element list and used; the other two are ignored.
  2. Merge Data (List of Records) — used only if no single record was provided.
  3. Merge Data (JSON) — used only if neither of the above was provided.

Use the single-record or list inputs when your merge data consists of real Salesforce records — they’re typed, so Flow handles them natively with no serialization. Use the JSON input only when the data isn’t a list of real sObjects (for example, synthetic rows with custom keys that don’t map to any object).

Response outputs (MergeResponse)

Variable Type Description
Job ID String The ID of the queued batch job. Populated on success.
Batches Integer The number of batches. Populated on success.
Batch Size Integer The batch size used. Populated on success.
Records Count Integer The number of records to be merged. Populated on success.
Error Type String The type of error, if one occurred.
Error Message String A human-readable error message, if one occurred.

To determine success in a Flow, check whether Error Message is blank. If it’s populated, the merge was not queued and Job ID will be empty.

Using it in Flow Builder

  1. Add an Action element to your Flow.
  2. Search for Mambo Merge: Start Merge (under the Mambo Merge category).
  3. Set the inputs. At minimum, provide a Routine Key and Template Number (for single-record Routines, also a Record ID; for multi-record Routines, a Record IDs collection).
  4. After the action, branch on the Error Message output: if it’s not blank, route to error handling; otherwise continue, optionally storing Job ID for later status lookups.

A typical record-triggered flow on an Account might set Routine Key to a literal string, Template Number to 1, and Record ID to {!$Record.Id}.


Bulk and Governor Considerations

Every successful merge queues one batch job. Salesforce limits the number of batch jobs that can be queued within a single transaction, so:

  • In Apex loops, don’t call startMerge once per record when a Routine supports multiple records. Instead, collect the IDs and make a single call with the recordIds list.
  • In Flows, be cautious about invoking this action in a bulk record-triggered context. If the same action fires for many records in one transaction, each invocation queues its own batch and you can quickly exceed the per-transaction limit. Prefer driving a single invocation with a populated Record IDs collection over fanning out to many single-record invocations.

When a merge spans many records, the work is automatically split into batches; the returned batches and batchSize values tell you how it was divided.


Error Handling Summary

Where How errors surface
Apex (startMerge) Returned in the result map under the batchableError key. Check for the key; don’t rely solely on try/catch.
Invocable (startMergeInvocable) Returned per-request in the Error Message and Error Type outputs. Check whether Error Message is blank.

Common initialization errors include: a missing or unknown routineKey, a Routine not enabled for Apex, a required recordId/recordIds/fileId not supplied, a fileId that isn’t a ContentDocument or ContentVersion, a record whose object type doesn’t match the Routine’s primary object, and an out-of-range templateNumber. The error message states which condition failed.