Skip to main content
Version: 4.9.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.

Priority and queues

  • priority defaults to 100. Lower value = higher priority when multiple registrations or listeners compete.
  • queue Omit this field to use the system default queue for event processing.
  • Decision criteria for custom queues:
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 other✅ Custom queue with sequential mode
Isolation from other apps' event traffic✅ Custom queue

The default queue processes events in parallel across multiple workers. A custom queue lets you control processing mode (parallel or sequential), scale factor, and poll interval independently.

→ See Custom Queue Registration below for detailed setup instructions, queue naming conventions, configuration options, and examples.


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

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

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:

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

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.

Queue Naming Convention

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

⛔ CRITICAL: Pre-Installation Uniqueness Check

Before creating any queue record, you MUST query the sysevent_queue table to verify your intended queue name does not already exist. Queue names must be unique across the entire table. Duplicate queue names cause conflicts in event routing and processing.

Always query in the instance before building/installing:

Query sysevent_queue with encodedQuery: queue=<your_intended_queue_name>

If a conflict is found:

Application TypeResolution
Scoped appChange both the suffix and the queue value to <scope>.<new_suffix>
Global appChange the queue name directly

Example conflicts:

  • Intended: sn_my_app.email_queue → already exists → Change to: sn_my_app.email_notification_queue
  • Intended: batch_processor → already exists → Change to: async_batch_processor

Choosing the Right Configuration

Use this decision matrix to select appropriate queue settings based on your use case:

Use CasePoll IntervalProcessing OrderJobsProvider
General event handling30sparallel1Event Provider
High-volume processing2–5sparallel2-3NowMQ Provider
Ordered task execution10–30ssequential1Event Provider
Background batch jobs1–10 minparallel1Event Provider
Low-latency interactive flows1–2sparallel2NowMQ Provider

Pre-Installation Checklist

Before building and installing your custom queue:

  1. Query sysevent_queue for your intended queue name — ensure it doesn't already exist
  2. Include sys_class_name: "sysevent_queue" in the record data (required for installation)
  3. Set suffix if scoped app; omit suffix if global app
  4. Use <scope>.<suffix> format for queue in scoped apps; plain name for global
  5. Verify the provider sys_id exists on the target instance
  6. Build and install
  7. Post-install verification — query sysevent_queue to confirm the record was created

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

Tip: Use Event Provider for standard use cases. Use NowMQ Provider for high-throughput, low-latency scenarios.

Poll Interval Format

The poll_interval field uses the glide_duration type with the format "1970-01-01 HH:mm:ss".

Common values:

DurationValue
1 second"1970-01-01 00:00:01"
5 seconds"1970-01-01 00:00:05"
30 seconds"1970-01-01 00:00:30"
1 minute"1970-01-01 00:01:00"
10 minutes"1970-01-01 00:10:00"

Configuration Reference

job_config Options

ValueBehavior
jobs_per_nodejob_config_value specifies the number of jobs per application node
job_countjob_config_value specifies the total number of jobs across all nodes

processing_order Options

ValueBehavior
parallelEvents are processed concurrently (faster, no ordering guarantee)
sequentialEvents are processed one at a time in order (slower, order preserved)

Examples

Example 1: Scoped App — Basic Parallel Processing

A standard scoped event queue with 30-second polling and parallel processing.

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',
},
});

Example 2: Scoped App — Sequential Batch Processing

A scoped queue for ordered batch jobs that must be processed one at a time to avoid race conditions.

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

export const batchQueue = Record({
$id: Now.ID['my_app_batch_queue'],
table: 'sysevent_queue',
data: {
sys_class_name: 'sysevent_queue',
queue: 'sn_my_app.batch_processing',
suffix: 'batch_processing',
description: 'Sequential batch processing queue — events processed one at a time in order.',
poll_interval: '1970-01-01 00:01:00',
job_config: 'jobs_per_node',
job_config_value: 1,
provider: '44af4464431212108da9a574a9b8f2f5',
automatic_processing: true,
processing_order: 'sequential',
},
});

Example 3: Scoped App — High-Throughput Queue with NowMQ Provider

A fast scoped queue using the in-memory NowMQ provider for high-volume event processing.

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

export const highThroughputQueue = Record({
$id: Now.ID['my_app_fast_queue'],
table: 'sysevent_queue',
data: {
sys_class_name: 'sysevent_queue',
queue: 'sn_my_app.fast_processing',
suffix: 'fast_processing',
description: 'High-throughput queue using NowMQ for low-latency event processing.',
poll_interval: '1970-01-01 00:00:02',
job_config: 'jobs_per_node',
job_config_value: 3,
provider: '3ccf4464431212108da9a574a9b8f2fb',
automatic_processing: true,
processing_order: 'parallel',
},
});

Example 4: Global App — Simple Event Queue

A global application queue — no suffix, plain queue name.

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

export const globalEventQueue = Record({
$id: Now.ID['global_email_queue'],
table: 'sysevent_queue',
data: {
sys_class_name: 'sysevent_queue',
queue: 'custom_email_notifications',
description: 'Global queue for processing email notification events.',
poll_interval: '1970-01-01 00:00:05',
job_config: 'job_count',
job_config_value: 2,
provider: '44af4464431212108da9a574a9b8f2f5',
automatic_processing: true,
processing_order: 'parallel',
},
});

Note: Global queues do not have a suffix field.

Troubleshooting

SymptomCauseFix
Install succeeds but queue record missingMissing sys_class_name in record dataAdd sys_class_name: 'sysevent_queue'
Build error on poll_intervalWrong format for glide_durationUse "1970-01-01 HH:mm:ss" format
Queue name doesn't appear correctlyMissing scope prefix in queue nameUse <scope>.<suffix> naming convention for scoped apps
Record not found after first installRare install timing issueReinstall — the record should appear on retry
Events not routing to custom queueQueue name mismatch between event and queueVerify exact queue name matches in both records
Duplicate queue name conflictQueue name already exists on instanceQuery sysevent_queue first; change suffix (scoped) or queue name (global)
"Event is not defined" errorsevent_name exceeded 40 charsShorten <scope>.<suffix> to fit within 40 characters