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 class | MGCLatticeCustomAdHocBusinessEvent |
| Contract class | MGCLatticeCustomAdHocBusinessEventContract |
| Category | System administration |
| Module | ModuleAxapta::SystemAdministration |
| Label IDs | CustomAdHocBusinessEventName / CustomAdHocBusinessEventDescription |
| Trigger | MGCLatticeCustomAdHocEventApi::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:
_eventNamemust be non-empty; otherwise it returns"An event name is required."._tableNamemust be an AOT table name (resolved withtableName2id); otherwise it returns"Unknown table '<name>'.".- 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>.". - 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 checkBusinessEventsConfigurationReader::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)), insideraiseEvent.
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 field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
eventName | Event Name | str | _eventName argument | Your logical event name; use it to route emissions in the subscriber |
tableName | Table Name | str | tableId2name(record.TableId) | AOT name of the referenced table |
recId | Record Id | RefRecId | record.RecId | Record 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 |
payloadJson | Payload Json | str | _payloadJson argument | Passed 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.