Skip to main content

Custom ad-hoc business event

A general-purpose business event that your own X++ code raises for any record, without authoring a dedicated event class for each case. Each emission carries a logical event name you choose, a reference to the record (table name and RecId), and a free-form JSON payload, so one catalog entry can serve many small integration signals - for example "credit review requested" on a customer or "label reprint needed" on a shipment. Lattice itself never raises it; it fires only when your code calls the API described below.

Event classMGCLatticeCustomAdHocBusinessEvent
Contract classMGCLatticeCustomAdHocBusinessEventContract
CategorySystem administration
ModuleModuleAxapta::SystemAdministration
Label IDsCustomAdHocBusinessEventName / CustomAdHocBusinessEventDescription
TriggerMGCLatticeCustomAdHocEventApi::raiseEvent() -> called from customer X++ code

Trigger / Hook​

  • Entry point: MGCLatticeCustomAdHocEventApi::raiseEvent(str _eventName, str _tableName, RefRecId _recId, str _payloadJson), a public static method that returns a short status message. It is the only send site in the package. The messages are labels, so they come back in the user's language; the English texts are quoted below. Show them to a user or log them, but don't parse them: check the event's activation yourself if your code needs to branch on it.
  • Firing condition, in order:
    1. _eventName must be non-empty; otherwise it returns "An event name is required.".
    2. _tableName must be an AOT table name (resolved with tableName2id); otherwise it returns "Unknown table '<name>'.".
    3. The record is read with select firstOnly ... where RecId == _recId. That select isn't cross-company, so for a company-specific table the record must belong to the company the code is running in; otherwise it returns "No record found in '<table>' with RecId <recId>.".
    4. If the event is activated, it builds the event with MGCLatticeCustomAdHocBusinessEvent::newFromReference(_eventName, record, _payloadJson), sends it, and returns a confirmation; if not, it returns "Ad-hoc business event '<name>' is not enabled; nothing was sent.".
  • How to raise it: call the API from your own extension code - for example from a Chain of Command extension, an event handler, or a batch class - in a model that references Lattice. It's an X++ API only: it isn't an OData action, and no service in the package exposes it, so it can't be called over OData or a service endpoint. To raise the event from outside Finance and Operations, expose the call through your own custom service.
  • Code that needs to skip the API's checks can call MGCLatticeCustomAdHocBusinessEvent::newFromReference(eventName, record, payloadJson).send() directly; it should then check BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomAdHocBusinessEvent)) itself.
  • Timing: send() writes to the business events commit log. If the calling code has a transaction open, the event is delivered only after that transaction commits and never if it's rolled back.
  • Enablement gate: BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomAdHocBusinessEvent)), inside raiseEvent.
public static str raiseEvent(str _eventName, str _tableName, RefRecId _recId, str _payloadJson)
{
...
Common record = MGCLatticeCustomAdHocEventApi::findRecord(tableId, _recId);

if (!record || !record.RecId)
{
return strFmt("@MGCLattice:AdHocEventRecordNotFound", _tableName, int642str(_recId));
}

if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomAdHocBusinessEvent)))
{
MGCLatticeCustomAdHocBusinessEvent::newFromReference(_eventName, record, _payloadJson).send();

return strFmt("@MGCLattice:AdHocEventRaised", _eventName, _tableName, int642str(_recId));
}

return strFmt("@MGCLattice:AdHocEventNotEnabled", _eventName);
}

Calling it from your own code:

str result = MGCLatticeCustomAdHocEventApi::raiseEvent(
'CreditReviewRequested',
tableStr(CustTable),
custTable.RecId,
'{"requestedBy":"Admin","reason":"Order exceeds 90% of credit limit"}');

Payload Contract​

One row per [DataMember], in declaration order. Values are captured in MGCLatticeCustomAdHocBusinessEvent::newFromReference() and copied to the contract in initialize().

JSON fieldDisplay labelType / EDTSourceNotes
eventNameEvent Namestr_eventName argumentYour logical event name; use it to route emissions in the subscriber
tableNameTable NamestrtableId2name(record.TableId)AOT name of the referenced table
recIdRecord IdRefRecIdrecord.RecIdRecord ID of the referenced record; record IDs are local to the environment, so match the record on your own data in payloadJson when the subscriber spans environments
payloadJsonPayload Jsonstr_payloadJson argumentPassed through unchanged and not validated; it arrives as an escaped JSON string, not a nested object, so parse it in the subscriber

Every Lattice event also carries the Lattice ordering and correlation envelope, inherited from MGCLatticeBusinessEventContractBase: eventSequence (Int64 - increasing across the whole catalog and all companies, allocated when the event is raised; it orders the events of one record, gaps are normal and permanent, see the README), correlationId (String - always Scope:Key; groups emissions that belong to one logical run and defaults to Event: plus a GUID unique to this emission) and schemaVersion (Int - the payload shape version; 1 for this contract). D365 F&O also adds its standard envelope fields to every business event payload: BusinessEventId, BusinessEventLegalEntity, ContextRecordSubject, ControlNumber, EventId, EventTime, EventTimeIso8601, InitiatingUserAADObjectId, MajorVersion, MinorVersion, and ParentContextRecordSubjects. Do not rely on the order of members in the delivered JSON.

This event doesn't call correlateTo, so correlationId is unique to each emission.

Example Payload​

The call shown above, for customer US-004 in USMF:

{
"BusinessEventId": "MGCLatticeCustomAdHocBusinessEvent",
"ControlNumber": 5637151207,
"EventId": "9D3B6A10-5E27-4C84-B1F9-2A7E6C0D8F45",
"EventTime": "\/Date(1790262224000)\/",
"EventTimeIso8601": "2026-09-24T15:03:44.5189390Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{85D4C607-4AFB-96CF-4C4A-D27D90CCA254}",
"ParentContextRecordSubjects": [],
"eventSequence": 48701,
"correlationId": "Event:b2c47e8d-1f63-4a09-9d5e-3c8a7f2b6e14",
"schemaVersion": 1,
"eventName": "CreditReviewRequested",
"tableName": "CustTable",
"recId": 22565421189,
"payloadJson": "{\"requestedBy\":\"Admin\",\"reason\":\"Order exceeds 90% of credit limit\"}"
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Custom ad-hoc business event") or by filtering the Category column to System administration. Select it and use Activate to bind it to a configured endpoint (for example, an Azure Service Bus queue, Event Grid topic, or HTTPS webhook).

Activation alone produces nothing: the event is raised only by X++ code you add that calls MGCLatticeCustomAdHocEventApi::raiseEvent(). Because every ad-hoc signal shares this one catalog entry, filter or route on eventName in the subscriber.