Skip to main content
Version: 4.6.0

Registering Events Guide

Guide for registering custom ServiceNow events in the Event Registry (sysevent_register) using the Record API, including custom queue configuration for high-volume event processing. Use when a custom event name is referenced, when building event-driven applications, or when the user mentions event registry, custom events, gs.eventQueue(), or custom queues.

When to Use

  • Use proactively as a prerequisite step whenever a custom event name is referenced. The event must be registered before it can be fired or consumed.
  • When building event-driven applications that need to fire or consume custom events.
  • When the user mentions "event registry", "register event", "custom event", gs.eventQueue(), or sysevent_register.
  • When the user mentions "custom queue", "event queue", or high-volume event processing.

When to Use Event-Based Approach

ScenarioUse Event-Based?
Single reaction to a record changeNo -- direct action is simpler
One trigger, multiple independent reactionsYes
Passing contextual data to downstream consumersYes
Asynchronous processing that shouldn't block the userYes

Instructions

  1. Use the Record API: There is no dedicated Fluent plugin for sysevent_register. Always use Record() with table: 'sysevent_register'.
  2. Scope-aware field usage:
    • Global apps: Set event_name directly. Leave suffix blank.
    • Scoped apps: Set both suffix AND event_name: '<scope>.<suffix>' explicitly. The platform does not auto-generate event_name on initial Record API creation.
  3. CRITICAL -- 40-character limit on event_name: Values longer than 40 characters are silently truncated. Always verify <scope>.<suffix> fits within 40 characters.
  4. Derive $id from the event name: Use a stable $id that maps 1-to-1 with the event name to prevent duplicate records on rebuild.
  5. Never set derived_priority: It is read-only and auto-computed.
  6. Register before firing: The event must exist before any code calls gs.eventQueue() or fires from Flow Designer.
  7. Always populate fired_by: Document what fires the event for maintainability.
  8. Always export the Record when used with flows: So flows can import and reference myEvent.$id.
  9. Use custom queues for high-volume events: Prevents flooding the default processor and allows serial processing to avoid race conditions.

Avoidance

  • Never omit event_name in scoped apps -- always set it explicitly as <scope>.<suffix> alongside suffix.
  • Never omit suffix in scoped apps -- it is mandatory alongside event_name.
  • Never modify suffix on existing scoped events -- changing it regenerates event_name and breaks all listeners.
  • Never exceed 40 characters for event_name -- silently truncated with no warning.
  • Never use two different $id values for the same event_name -- creates duplicate registrations causing listeners to fire twice.
  • Never fire an event before registering it -- fails silently in scoped apps.
  • Never leave fired_by blank.
  • Never pass priority as a String -- the field expects an integer.
  • Never forget to export the event Record when referenced by a flow.

Event Registry API Reference

Register events using Record() with table: 'sysevent_register'.

Data Fields

NameTypeMandatoryDescription
event_nameString (max 40)--Primary identifier. Scoped: set as <scope>.<suffix>. Global: set directly.
suffixString (max 40)Yes (scoped)Mandatory in scoped apps alongside event_name. Not used in global apps.
descriptionString (max 100)--When and why the event fires.
tableString (max 80)--Associated ServiceNow table.
fired_byString (max 100)--What code fires the event (required for traceability).
priorityinteger--Processing order (lower = higher priority). Default: 100.
queueString--Custom queue name. Must match an existing sysevent_queue record.
caller_accessString (choice)--Cross-scope access: '' (none), '1' (tracking), '2' (restriction).
derived_priorityFloat--READ-ONLY. Never set this field.

Caller Access Values

ValueUI LabelBehavior
'' (default)NoneNo restriction. Any caller can fire the event.
'1'Caller TrackingCross-scope allowed but logged.
'2'Caller RestrictionCross-scope blocked by default; admin approval required.

Examples

Scoped App Event Registration

import { Record } from '@servicenow/sdk/core';

Record({
$id: Now.ID['x-myapp-incident-approved-event'],
table: 'sysevent_register',
data: {
suffix: 'incident.approved',
event_name: 'x_myapp.incident.approved',
description: 'Fired when an incident is approved in My App',
table: 'incident',
fired_by: 'Business Rule: My App Incident Approvals',
priority: 200,
},
});

Global App Event Registration

import { Record } from '@servicenow/sdk/core';

Record({
$id: Now.ID['incident-approved-event'],
table: 'sysevent_register',
data: {
event_name: 'incident.approved',
description: 'Fired when an incident is approved',
table: 'incident',
fired_by: 'Business Rule: Incident Approvals',
priority: 100,
},
});

Event with Caller Tracking

Record({
$id: Now.ID['x-myapp-contract-expiring-event'],
table: 'sysevent_register',
data: {
suffix: 'contract.expiring',
event_name: 'x_myapp.contract.expiring',
description: 'Fired when a contract is within 30 days of expiry',
table: 'x_myapp_contract',
fired_by: 'Business Rule: Contract Expiry Check',
priority: 100,
caller_access: '1',
},
});

Event-Driven Flow with Email Notification

Three-file pattern: Event Registration, Flow (fireEvent), Email Notification.

File 1: Register and export the event:

export const employeeTerminatedEvent = Record({
$id: Now.ID['sn-myapp-employee-terminated-event'],
table: 'sysevent_register',
data: {
suffix: 'employee.terminated',
event_name: 'sn_myapp.employee.terminated',
description: 'Fired when employee status changes to Terminated',
table: 'sn_myapp_employee_record',
fired_by: 'Flow: Notify manager on termination',
priority: 100,
},
});

File 2: Flow fires the event using employeeTerminatedEvent.$id (resolves to sys_id).

File 3: Notification references event by name string 'sn_myapp.employee.terminated'.

Critical difference: The Flow uses .$id (sys_id reference). The Notification uses the event name string. Mixing them up causes errors.

Event-Driven Business Rule with Script Action

Four-file pattern: Event Registration, Business Rule (gs.eventQueue), ScriptAction, Email Notification.

The business rule fires via gs.eventQueue('sn_myapp.new.employee.added', current, parm1, parm2). The ScriptAction and EmailNotification both reference the full event name string in their eventName fields.


Custom Queue Registration

Use a custom queue when your app generates high volumes of events or when events must be processed serially.

When to Use a Custom Queue

ScenarioRecommendation
Low/moderate frequency (tens per hour)Default queue is sufficient
High volume (hundreds/thousands per batch)Custom queue to isolate traffic
Events that must not race each otherCustom queue with sequential mode
Isolation from other apps' event trafficCustom queue

Queue Naming Convention

Scoped apps: <scope_name>.<suffix> (suffix is mandatory). Global apps: Plain unique name (no suffix field).

CRITICAL: Queue names must be unique. Always query sysevent_queue before creating.

Custom Queue Properties

FieldTypeMandatoryDescription
sys_class_namestringYesMust be "sysevent_queue" for correct installation.
queuestringYesFull queue name.
suffixstringScoped: YesPortion after scope prefix.
poll_intervalglide_durationYesFormat: "1970-01-01 HH:mm:ss".
job_configstringNo"jobs_per_node" or "job_count".
job_config_valueintegerYesNumber of concurrent jobs.
providerreferenceYessys_id of sysevent_queue_provider record.
processing_orderstringNo"parallel" or "sequential".

Available Providers

Providersys_idDescription
Event Provider44af4464431212108da9a574a9b8f2f5Default; uses Processing Framework
NowMQ Provider3ccf4464431212108da9a574a9b8f2fbIn-memory queue for high throughput

Custom Queue Example

import { Record } from '@servicenow/sdk/core';

// Step 1: Create the queue
Record({
$id: Now.ID['my_app_custom_queue'],
table: 'sysevent_queue',
data: {
sys_class_name: 'sysevent_queue',
queue: 'sn_my_app.custom_queue',
suffix: 'custom_queue',
description: 'General-purpose event queue for my application.',
poll_interval: '1970-01-01 00:00:30',
job_config: 'jobs_per_node',
job_config_value: 1,
provider: '44af4464431212108da9a574a9b8f2f5',
automatic_processing: true,
processing_order: 'parallel',
},
});

// Step 2: Register the event with the queue name
Record({
$id: Now.ID['sn_my_app-contract-expiring-event'],
table: 'sysevent_register',
data: {
suffix: 'contract.expiring',
event_name: 'sn_my_app.contract.expiring',
description: 'Fired when a contract is within 30 days of expiry',
table: 'sn_my_app_contract',
fired_by: 'Business Rule: Contract Expiry Check',
priority: 100,
queue: 'sn_my_app.custom_queue',
},
});

Troubleshooting

SymptomCauseFix
Install succeeds but queue record missingMissing sys_class_nameAdd sys_class_name: 'sysevent_queue'
Events not routing to custom queueQueue name mismatchVerify exact name matches
Duplicate queue name conflictName already existsChange suffix (scoped) or queue name (global)
"Event is not defined" errorsevent_name exceeded 40 charsShorten <scope>.<suffix> to fit within 40