Repository navigation
platform sdk custom events
Applications can raise custom events on entities, monitor them in real time, and use them to trigger actions.
Custom events are user-defined events that supplement the built-in Security Center events. They allow you to:
- Define events specific to your integration or workflow
- Raise events on any entity in the system
- Display events in the Monitoring task
- Trigger event-to-action rules (see Event-to-actions)
Access the ICustomEventService through the SystemConfiguration entity:
var systemConfig = (SystemConfiguration)engine.GetEntity(SystemConfiguration.SystemConfigurationGuid);
ICustomEventService customEventService = systemConfig.CustomEventService;Custom events have an owner that determines who can delete them:
| Owner | Created by | Can be deleted by |
|---|---|---|
| None (local) | SDK applications | Anyone with the GeneralSettingsEvents privilege |
| Plugin | Plugin roles | Anyone with the GeneralSettingsEvents privilege |
| Federated system | External Security Center | Cannot be deleted locally |
When a plugin creates a custom event, the event is automatically associated with that plugin's subtype GUID. This ensures the custom event remains available as long as the plugin is registered. Because it is a local custom event, any caller with the GeneralSettingsEvents privilege can remove it.
SDK applications create local custom events with no owner. These events can be deleted by any user with sufficient privileges.
Custom events from federated Security Center systems appear in your system but cannot be modified or deleted locally.
Use the Owner property to get the GUID of the custom event's owner:
CustomEvent evt = customEventService.GetCustomEvent(1001);
Guid owner = evt.Owner;For local events created by SDK applications, Owner returns Guid.Empty. For plugin-owned events, Owner returns the plugin's subtype GUID. For federated events, Owner returns the federation role GUID.
Use the LocalCustomEvent property to check whether a custom event can be deleted:
if (evt.LocalCustomEvent)
{
customEventService.Remove(evt);
}This property returns true for local and plugin-owned events, and false for federated events. You can remove events only when this property returns true.
Use ICustomEventBuilder to define a new custom event:
ICustomEventBuilder builder = customEventService.CreateCustomEventBuilder();
CustomEvent customEvent = builder
.SetName("Equipment Malfunction")
.SetEntityType(EntityType.CustomEntity)
.Build();The SetName() and SetEntityType() methods are required. The entity type specifies which entities can raise this event.
By default, the system assigns the next available ID when the event is added. To specify a particular ID:
CustomEvent customEvent = builder
.SetName("Equipment Malfunction")
.SetEntityType(EntityType.CustomEntity)
.SetId(1001)
.Build();After building a custom event, add it to the system:
CustomEventUpdateResult result = customEventService.Add(customEvent);
if (result.Success)
{
int assignedId = result.Id;
}The CustomEvent.Id property is also updated with the assigned ID after a successful add.
Adding, updating, and removing custom events require the caller to hold the GeneralSettingsEvents privilege. If the current user lacks the privilege, these methods throw an SdkException with SdkError.UnsufficientPrivilege. Building an event with ICustomEventBuilder does not require the privilege.
Custom event names must be unique. Add throws an ArgumentException if an event with the same name already exists. Choose a distinct name or update the existing event instead.
When called from a plugin, the custom event is automatically owned by that plugin. When called from an SDK application, the custom event is local (no owner).
Plugins can create local custom events instead of plugin-owned events by passing Guid.Empty:
CustomEventUpdateResult result = customEventService.Add(Guid.Empty, customEvent);Use AddAsync to add multiple custom events in one asynchronous operation:
CustomEvent event1 = builder.SetName("Event One").SetEntityType(EntityType.Camera).Build();
CustomEvent event2 = builder.SetName("Event Two").SetEntityType(EntityType.Door).Build();
CustomEventUpdateResult[] results = await customEventService.AddAsync(event1, event2);CustomEvent.Id is the value used to reference the custom event definition in SDK operations. Name is stored display text, and Security Center does not resolve it through a per-client resource key. Native Security Center pages display the same stored name after the client language changes.
Create one custom event for the event meaning, not one event per language. Use its ID in integration logic. A custom Workspace view can map that ID to an integration resource key and resolve the label with Workspace.Culture. For this pattern, see Workspace SDK localization.
After creating custom events, you can inspect the full collection or retrieve individual definitions by identifier.
IReadOnlyList<CustomEvent> allEvents = customEventService.CustomEvents;
foreach (CustomEvent evt in allEvents)
{
Console.WriteLine($"ID: {evt.Id}, Name: {evt.Name}, EntityType: {evt.SourceEntityType}");
}Retrieve by ID:
CustomEvent evt = customEventService.GetCustomEvent(1001);Retrieve by ID and owner (for plugin-owned custom events):
CustomEvent evt = customEventService.GetCustomEvent(1001, ownerRoleGuid);Retrieve by name:
CustomEvent evt = customEventService.GetCustomEvent("Equipment Malfunction");bool exists = customEventService.Contains(1001);Update a custom event's name using CustomEventUpdateInfo:
var updateInfo = new CustomEventUpdateInfo
{
Id = 1001,
Name = "Equipment Failure"
};
CustomEventUpdateResult result = customEventService.Update(updateInfo);To get the updated CustomEvent object:
CustomEventUpdateResult result = customEventService.Update(updateInfo, out CustomEvent updatedEvent);Remove by ID:
CustomEventUpdateResult result = customEventService.Remove(1001);Remove by name:
CustomEventUpdateResult result = customEventService.Remove("Equipment Malfunction");Remove by object:
CustomEventUpdateResult result = customEventService.Remove(customEvent);You can remove any local custom event, whether it has no owner or is plugin-owned, if you have the GeneralSettingsEvents privilege. Custom events from a federated system are not local and cannot be removed.
To raise a custom event on an entity:
- Build the event using
ActionManager.BuildEvent. - Cast to
CustomEventInstanceand set the event ID and message. - Call
RaiseEvent.
var eventInstance = (CustomEventInstance)engine.ActionManager.BuildEvent(
EventType.CustomEvent,
sourceEntityGuid);
eventInstance.Id = new CustomEventId(1001);
eventInstance.Message = "Sensor detected anomaly at 14:30";
engine.ActionManager.RaiseEvent(eventInstance);Custom event IDs must be 1 or greater. Passing 0 or a negative value to CustomEventId throws an ArgumentException.
An Access Manager role records custom events raised on supported access control entities that it manages. By default, the role excludes events raised on source entities owned by a plugin. This restriction concerns the source entity, not the owner of the custom event definition.
To retrieve a recorded event, use the activity report query that matches the source entity type:
| Source entity | Native report query | Key result columns |
|---|---|---|
| Access-control area | AreaActivityQuery |
SourceGuid, AccessPointGroupGuid, CustomEventMessage
|
| Cardholder | CardholderActivityQuery |
SourceGuid, CardholderGuid, CustomEventMessage
|
| Credential | CredentialActivityQuery |
SourceGuid, CredentialGuid, CustomEventMessage
|
| Door | DoorActivityQuery |
SourceGuid, AccessPointGroupGuid, CustomEventMessage
|
To query only specific custom events, create a new activity report query. Before executing it, clear its Events and CustomEvents collections, and then add the required IDs to CustomEvents. Create a new query when changing the event filters after execution. Door activity queries include event filters by default.
Use the positive custom event ID when you filter a report query by custom event. The returned row exposes the same custom event ID as a negative EventType value. For example, a custom event with ID 1001 appears as -1001 in the activity report results.
AccessControlRawEventQuery can also show that the Access Manager recorded the event in its raw stream, but it is an incremental stream query and does not expose CustomEventMessage. Use the native activity report when your application needs the custom event message or the entity-specific report columns.
For area events, use an access control area that participates in the access control topology. Use the activity report queries listed above to retrieve recorded custom events.
A camera associated with a door-side access point or an area remains the source of any custom event raised on that camera. These associations do not cause the Access Manager role to record camera-sourced custom events. To create a door activity record, raise the custom event on the door.
Associate additional entities with the event:
eventInstance.AddRelatedEntity(relatedCameraGuid);
eventInstance.AddRelatedEntities(new[] { entity1Guid, entity2Guid });Send data that is not displayed in the user interface (UI) but is available to SDK applications:
eventInstance.ExtraHiddenPayload = "{\"sensorId\": 42, \"reading\": 98.6}";Subscribe to the Engine.EventReceived event and check for CustomEventReceivedEventArgs:
engine.EventReceived += OnEventReceived;
private void OnEventReceived(object sender, EventReceivedEventArgs e)
{
if (e is CustomEventReceivedEventArgs customEventArgs)
{
int eventId = customEventArgs.CustomEventId;
string message = customEventArgs.Message;
string hiddenPayload = customEventArgs.ExtraHiddenPayload;
Guid sourceEntity = customEventArgs.SourceGuid;
Console.WriteLine($"Custom event {eventId}: {message}");
}
}Note
To receive events for an entity, that entity must be loaded in the Engine entity cache. Use engine.GetEntity to load the entity before expecting to receive its events.
Subscribe to changes in the custom event definitions. The handler in this example refreshes the custom event collection after a change:
var systemConfig = (SystemConfiguration)engine.GetEntity(SystemConfiguration.SystemConfigurationGuid);
systemConfig.CustomEventsChanged += OnCustomEventsChanged;
private void OnCustomEventsChanged(object sender, EventArgs e)
{
IReadOnlyList<CustomEvent> events = systemConfig.CustomEventService.CustomEvents;
}When defining a custom entity type, specify which custom events it supports:
var descriptor = new CustomEntityTypeDescriptor(
id: customTypeGuid,
name: "Evidence Locker",
capabilities: CustomEntityTypeCapabilities.IsVisible,
version: new Version(1, 0, 0));
descriptor.SupportedCustomEvents = new[] { 1001, 1002, 1003 };- Working with Security Center events: Receiving operational events from Security Center.
- About the ReportManager: Creating and executing report queries.
- Access control raw events: Reading the raw Access Manager event stream.
- Receiving actions: Receiving actions directed at entities.
- Overview
- Connecting to Security Center
- SDK certificates
- Referencing SDK assemblies
- ASP.NET hosting
- SDK compatibility
- Migrating to modern .NET
- Entities
- Entity cache
- Entity loading, cache, and query options
- Detecting entity changes
- Entity change event reference
- Transactions
- Security Center events
- Actions
- Security Desk
- Custom events
- Event-to-actions
- Managing macros
- ReportManager
- ReportManager query reference
- Access control raw events
- DownloadAllRelatedData and StrictResults
- Privileges
- Partitions
- Entity write restrictions
- About custom fields
- About video
- About cameras
- Enrolling a video unit
- Ingesting third-party video
- Archiver and auxiliary archiver roles
- Archive transfer
- About access control
- About cardholders and credentials
- About doors, areas, elevators, and access points
- About access rules and schedules
- About access control units and interface modules
- Enrolling an access control unit
- Door templates
- Visitors
- Mobile credentials
- About threat levels
- About alarms
- About incidents
- Maps
- Logging
- Overview
- Localization
- Certificates
- Lifecycle
- Threading
- State management
- Configuration
- Restricted configuration
- Events
- Queries
- Request manager
- Database
- Entity ownership
- Engine extensions
- Entity mappings
- Server management
- Custom privileges
- Custom entity types
- Resolving non-SDK assemblies
- Deploying plugins
- Modern .NET support
- Overview
- Localization
- Certificates
- Creating modules
- Tasks
- Pages
- Components
- Tile extensions
- Services
- Contextual actions
- Custom actions
- Options extensions
- Configuration pages
- Monitors
- Shared components
- Commands
- Extending events
- Map extensions
- Timeline providers
- Image extractors
- Credential encoders
- Credential readers
- Cardholder fields extractors
- Badge printers
- Content builders
- Dashboard widgets
- Incidents
- Logon providers
- Pinnable content builders
- Custom report pages
- Overview
- Getting started
- Media file formats
- MediaPlayer
- VideoSourceFilter
- MediaExporter
- MediaFile
- G64 converters
- FileCryptingManager
- PlaybackSequenceQuerier
- PlaybackStreamReader
- OverlayFactory
- PtzCoordinatesManager
- AudioTransmitter
- AudioRecorder
- AnalogMonitorController
- Camera blocking
- Overview
- Getting started
- Referencing entities
- Entity operations
- About access control in the Web SDK
- About video in the Web SDK
- Users and user groups
- Partitions
- Custom fields
- Custom card formats
- Actions
- Events and alarms
- Incidents
- Reports
- Tasks
- Macros
- Custom entity types
- System endpoints
- Performance guide
- Reference
- Endpoints
- Constants
- Enumerations
- Under the hood
- Troubleshooting