Skip to main content

Servicenow

@dougschaefer/servicenowv2026.10.08.1· 18d agoBEHAVIORS
01README

ServiceNow integration model — OAuth client_credentials authentication with automatic token caching, generic Table API CRUD (query, get, create, update, delete), Aggregate API for data analysis, Attachment API (list, download, upload, delete), and a passthrough REST call for endpoints outside the Table API. Foundation for spoke development, administration, and ad-hoc operations against any ServiceNow instance.

02Release Notes

Security fix. The OAuth token cache is now keyed by instance origin, client id and scope, so models pointed at different instances or OAuth clients in one process never share a token. Every request is refused unless its path resolves to the instance origin, closing protocol-relative paths (//host/x) that could send the bearer token off-host. A token response without access_token is refused instead of cached. Method docs and README examples corrected.

03Behaviors1
@dougschaefer/servicenow-instancev2026.10.08.1servicenow/instance.ts
fn lookup()
Verify OAuth credentials, capture instance metadata (release, scope prefix), and identify the user the token impersonates. Run this first to confirm connectivity.
fn tableQuery(table: string, query?: string, fields?: string)
Query a ServiceNow table with an encoded query string. Returns matching records as resource artifacts. Use sysparm_query syntax (e.g., 'active=true^stateIN1,2^ORDERBYsys_created_on').
ArgumentTypeDescription
tablestringTable name (e.g., 'incident', 'sys_user', 'sys_app')
query?stringEncoded query string (sysparm_query). Empty returns all records up to limit.
fields?stringComma-separated field list to retrieve (sysparm_fields). Defaults to all fields.
fn tableGet(table: string, sysId: string, fields?: string)
Fetch a single record from a ServiceNow table by sys_id.
ArgumentTypeDescription
tablestringTable name
sysIdstringsys_id of the record
fields?stringComma-separated field list (sysparm_fields). Defaults to all fields.
fn tableCreate(table: string, fields: record)
Insert a new record into a ServiceNow table. Pass field values as a JSON object.
ArgumentTypeDescription
tablestringTable name
fieldsrecordField name → value object for the new record
fn tableUpdate(table: string, sysId: string, fields: record)
Update fields on an existing ServiceNow record. Uses HTTP PATCH semantics — only provided fields are changed.
ArgumentTypeDescription
tablestringTable name
sysIdstringsys_id of the record to update
fieldsrecordField name → value object with only the fields to change
fn tableDelete(table: string, sysId: string)
Delete a record from a ServiceNow table by sys_id. Destructive — verify the sys_id with tableGet first.
ArgumentTypeDescription
tablestringTable name
sysIdstringsys_id of the record to delete
fn tableDeleteMany(records: array)
Fan-out: delete a batch of records (each a table + sys_id pair) in one execution — one model-lock acquisition instead of N tableDelete calls. Built for deprovisioning record sets recorded in a manifest (e.g. a customer's provisioned reports + dashboard canvas chain). Records already gone (404) are reported as 'absent', not errors — the method is idempotent and continues past them. Destructive — verify sys_ids against the source manifest first. Pass records via --stdin (nested array does not surv
ArgumentTypeDescription
recordsarrayRecords to delete, in order. Delete children before parents (e.g. canvas panes/prefs before portal pages before pa_dashboards) when cascade rules are uncertain.
fn tableAggregate(table: string, query?: string, count?: boolean, avgFields?: string, sumFields?: string, minFields?: string, maxFields?: string, groupBy?: string)
Aggregate query against a ServiceNow table (count, sum, avg, min, max, group_by). Use this for data analysis without dragging full record sets across the wire.
ArgumentTypeDescription
tablestringTable name
query?stringEncoded query (sysparm_query) to filter records before aggregation
count?booleanInclude record count (sysparm_count)
avgFields?stringComma-separated numeric fields to average (sysparm_avg_fields)
sumFields?stringComma-separated numeric fields to sum (sysparm_sum_fields)
minFields?stringComma-separated numeric fields for minimum (sysparm_min_fields)
maxFields?stringComma-separated numeric fields for maximum (sysparm_max_fields)
groupBy?stringComma-separated fields to group by (sysparm_group_by)
fn restCall(path: string, body?: unknown, params?: record)
Generic REST call to any path on the ServiceNow instance. Use for Scripted REST APIs, Import APIs, application-specific endpoints, or anything outside the Table API. The OAuth token is applied automatically.
ArgumentTypeDescription
pathstringURL path on the instance (e.g., '/api/sn_chg_rest/change' or '/api/x_example_app/widget')
body?unknownRequest body for POST/PUT/PATCH (object will be JSON-serialized)
params?recordQuery string parameters
fn attachmentList(tableName?: string, tableSysId?: string, query?: string)
List attachment metadata (sys_attachment) via the Attachment API, optionally narrowed to one table and/or one record. Writes each row as an attachment resource; use attachmentDownload to fetch the bytes.
ArgumentTypeDescription
tableName?stringOnly attachments on this table (e.g., 'incident')
tableSysId?stringOnly attachments on this record's sys_id
query?stringAdditional encoded query against sys_attachment (e.g., 'content_type=application/pdf'), ANDed with the table filters
fn attachmentDownload(sysId: string)
Download one attachment's bytes (GET /api/now/attachment/{sys_id}/file) and store them as an attachment-file data artifact. Fails without writing anything if the file is larger than maxBytes.
ArgumentTypeDescription
sysIdstringsys_attachment sys_id
fn attachmentUpload(tableName: string, tableSysId: string, fileName: string, filePath?: string, contentBase64?: string, contentType?: string)
Attach a file to a record (POST /api/now/attachment/file with a raw body). Supply exactly one of filePath (a regular file inside the repository on the host running the method, outside .swamp/ and vaults/) or contentBase64. Content-Type is taken from contentType or inferred from the file name extension. Rejects files larger than maxBytes before sending. Writes the new sys_attachment row as an attachment resource.
ArgumentTypeDescription
tableNamestringTable of the record to attach to (e.g., 'incident')
tableSysIdstringsys_id of the record to attach to
fileNamestringFile name the attachment is stored under (e.g., 'evidence.pdf')
filePath?stringPath of the file to upload, absolute or relative to the repository root. Must resolve (symlinks followed) to a regular file inside the repository, outside .swamp/ and vaults/. Read on the host running the method.
contentBase64?stringFile content as base64, as an alternative to filePath
contentType?stringMIME type to send. Defaults to one inferred from fileName, else application/octet-stream.
fn attachmentDelete(sysId: string)
Delete one attachment by sys_attachment sys_id (DELETE /api/now/attachment/{sys_id}). Destructive — confirm the sys_id with attachmentList first. An attachment that is already gone (404) counts as success.
ArgumentTypeDescription
sysIdstringsys_attachment sys_id of the attachment to delete
fn setupOAuthProvider(name: string, authUrl: string, tokenUrl: string, providerClientId: string, scopes: array, grantType?: string, usePkce?: boolean, codeChallengeMethod?: string, sysScope?: string, redirectUrl?: string, connectionAliasName?: string)
Fan-out: stand up a third-party OAuth 2.0 provider for an integration spoke in one execution (one model-lock acquisition). Creates the oauth_entity (Application Registry record), its default oauth_entity_profile, all oauth_entity_scope records (name + oauth_entity_scope both set), the profile↔scope m2m links, and optionally a shared Connection & Credential Alias — replacing ~3+N manual Table API calls. Set sysScope to land every record inside a scoped app (verified: sys_scope sticks on oauth_ent
ArgumentTypeDescription
namestringDisplay name for the OAuth provider (Application Registry record), e.g. 'Webex Spoke Integration'
authUrlstringProvider authorization endpoint URL (e.g., https://webexapis.com/v1/authorize)
tokenUrlstringProvider token endpoint URL (e.g., https://webexapis.com/v1/access_token)
providerClientIdstringOAuth client_id to configure on the provider. Named providerClientId (not clientId) to avoid collision with the model's global clientId. Use: ${{ vault.get(<vault>, <client-id-key>) }}
scopesarrayList of OAuth scope strings to request (each becomes an oauth_entity_scope linked to the profile)
grantType?stringOAuth grant type (default authorization_code)
usePkce?booleanEnable PKCE (default true)
codeChallengeMethod?stringPKCE code challenge method (default S256)
sysScope?stringScoped application sys_id to create the provider records IN. Omit for global scope. When set, sys_scope + sys_package are stamped on the oauth_entity, its oauth_entity_scope rows, and the profile↔scope m2m links so the provider ships as part of the scoped app. (Platform OAuth tables can be stricter about cross-scope writes than ordinary tables — verify the entity's resulting sys_scope after creation.)
redirectUrl?stringExplicit redirect_url for the oauth_entity. Omit to let ServiceNow auto-generate (…/oauth_redirect.do). Set it when the provider's pre-registered redirect must match exactly.
connectionAliasName?stringIf set, also create a shared Connection & Credential Alias (sys_alias: type=connection, connection_type=http_connection, multiple_connections=true) with this name/id — the alias that per-account connections attach to (see addOAuthAccount). Returned as aliasSysId.
fn addOAuthAccount(accountName: string, oauthEntityProfileSysId: string, connectionAliasSysId?: string, connectionAliasName?: string, connectionUrl: string, sysScope?: string, registryTable?: string, registryFields?: record, registryConnectionField?: string)
Per-account step for an interactive (authorization_code) OAuth provider: creates an OAuth 2.0 Credential bound to the provider's default profile, an HTTP Connection bound to a shared Connection & Credential Alias, and (optionally) an operational-registry row that references the connection. One call per account/tenant/org; many accounts share one alias (multiple_connections=true) and one oauth_entity, each holding its own token set — the multi-tenant pattern. The only remaining step is a one-time
ArgumentTypeDescription
accountNamestringHuman label for this account/org (e.g. 'Contoso Webex Org'). Used to name the credential and connection records.
oauthEntityProfileSysIdstringsys_id of the provider's default oauth_entity_profile (setupOAuthProvider returns this as profileSysId).
connectionAliasSysId?stringsys_id of an EXISTING Connection & Credential Alias to attach this connection to. Omit (recommended) to mint a dedicated PER-ORG alias — that keeps sn_cc.ConnectionInfoProvider.getConnectionInfo(alias) deterministic per tenant (one connection per alias). Pass a shared alias only for the multiple_connections pattern.
connectionAliasName?stringName/id for the per-org alias when one is minted (connectionAliasSysId omitted). Defaults to '<accountName>-alias' sanitized.
connectionUrlstringBase URL the connection targets, e.g. https://webexapis.com. host + protocol are derived from it.
sysScope?stringScoped application sys_id to create the credential, connection, and registry row IN. Omit for global (recommended: per-tenant OAuth wiring is customer-specific and must never enter an app's source or Store package). Enforced via session scope — the Table API ignores payload sys_scope on these tables.
registryTable?stringOptional operational-registry table to insert a tracking row into (e.g. x_example_app_webex_org). The created connection's sys_id is injected automatically.
registryFields?recordField map for the registry row (e.g. {name: 'Contoso', webex_org_id: '...'}) . Combined with the auto-injected connection reference. Pass via --stdin or a workflow step (nested objects don't survive --input key=value).
registryConnectionField?stringField on the registry row that should reference the created http_connection (default 'connection').
fn findOrCreateCompany(name: string, outputName?: string)
Find a ServiceNow Company (core_company) by exact name, or create it if absent. Returns the company sys_id — the per-customer discriminator used to scope multitenant device/alert/incident data and reports. Idempotent; safe to call every onboarding run.
ArgumentTypeDescription
namestringCompany (customer) name to find or create, matched exactly on core_company.name.
outputName?stringFixed name for the produced 'company' resource so a workflow can reference it via data.latest (e.g. 'customer-company'). Defaults to 'company-<sanitized name>'.
fn provisionCustomerReports(company?: string, org?: string, reportSet: array, sysScope?: string)
Fan-out: create a set of sys_report records, each optionally filtered to one customer's company (or org). Pass the report specs; the method appends the per-customer scope clause (when a company/org is given) and inserts them all in a single execution (one model-lock — the factory pattern, not N separate calls). Two modes: CUSTOMER (company set → every report filtered to it; create in GLOBAL scope by omitting sysScope, so customer-specific artifacts never enter the app source / Store package) and
ArgumentTypeDescription
company?stringcore_company sys_id — the per-customer discriminator each report is filtered by (unless a report item sets scopeBy='org'). Omit (or pass empty) for a GENERIC all-orgs set with no customer filter.
org?stringOptional webex_org (registry row) sys_id, used to scope any report item whose scopeBy='org'.
reportSetarrayReport specs. Each becomes a sys_report; the method appends '^<scopeField>=<sysId>' to the filter. Extra keys pass through to sys_report. Pass via --stdin (nested array does not survive --input key=value).
sysScope?stringScoped application sys_id to create the reports IN. Omit for global — the right choice for customer-specific sets, which must never ship with the app.
fn provisionCustomerDashboard(dashboardName: string, tabs: array, sysScope?: string)
Fan-out: assemble a complete responsive dashboard (Platform Analytics canvas) from already-provisioned sys_report records, in one execution. The canvas IS Table-API-buildable — the anatomy is pa_dashboards → per tab a sys_portal_page + sys_grid_canvas + pa_tabs + pa_m2m_dashboard_tabs, and per widget a sys_portal row + RenderReport sys_portal_preferences + a positioned sys_grid_canvas_pane (12-unit-wide grid; single scores ~h=8, charts ~h=14, lists ~h=16). Widgets reference reports by exact titl
ArgumentTypeDescription
dashboardNamestringDisplay name of the pa_dashboards record, e.g. 'Example Fleet Operations'.
tabsarray
sysScope?stringScoped application sys_id for the dashboard/tab/pane records. Omit for global.
fn commitAppSource(commitEndpoint: string, commitMessage: string, appSysId?: string)
Commit a scoped application's outgoing source-control changes to its linked git remote, server-side, and wait for completion. Wraps sn_vcs.AppSourceControl.commitOutgoingChanges() (the engine ServiceNow Studio's 'Commit Changes' calls) via a small commit endpoint the app exposes, then polls sys_execution_tracker until the commit+push finishes. This lets a deploy pipeline commit and push the ServiceNow app without a Studio click. The app must expose the commit endpoint as a dev-only Scripted REST
ArgumentTypeDescription
commitEndpointstringPath of the app's commit Scripted REST op (POST), e.g. '/api/x_example_app/source/commit-source'. It runs sn_vcs.AppSourceControl.commitOutgoingChanges(...).start() and returns { trackerId }.
commitMessagestringCommit message for the source-control commit.
appSysId?stringsys_app sys_id to commit; forwarded to the endpoint (falls back to the endpoint's own application if omitted).

Resources

instance-info(infinite)— ServiceNow instance metadata snapshot — release, build, scope prefix, and the user identity the OAuth token impersonates
record(infinite)— A single record from a ServiceNow table — full field set as returned by the Table API, tagged with table name and sys_id
aggregate(infinite)— Result of a ServiceNow Aggregate API query — count/avg/sum/min/max/group_by output
rest-response(infinite)— Response body from a generic ServiceNow REST call — for endpoints outside the Table API (Scripted REST APIs, Import APIs, custom application endpoints)
oauth-provider(infinite)— Summary of a third-party OAuth 2.0 provider stood up by setupOAuthProvider — the oauth_entity (Application Registry), its default profile, and the linked scopes, plus the remaining manual UI steps (Connection & Credential Alias + interactive token grant).
oauth-account(infinite)— A single per-account OAuth wiring created by addOAuthAccount — the OAuth 2.0 Credential (bound to the provider profile), the HTTP Connection (bound to the shared alias), and an optional operational-registry row, plus the interactive Get-OAuth-Token consent URL.
company(infinite)— A ServiceNow Company (core_company) resolved or created by findOrCreateCompany — the per-customer discriminator used to scope multitenant device/alert/incident data and reports.
report-set(infinite)— The set of sys_report records provisioned by provisionCustomerReports — filtered to one customer's company (or org), or an unfiltered generic all-orgs set when no company was given.
delete-batch(infinite)— Outcome manifest of a tableDeleteMany run — per-record deleted/absent/error outcomes for a batch deprovisioning, kept for verification and audit.
dashboard-canvas(infinite)— A responsive dashboard (Platform Analytics canvas) assembled record-by-record by provisionCustomerDashboard — the pa_dashboards row, per-tab page/canvas/tab sys_ids, and every widget's sys_portal + pane sys_ids, for verification and rollback.
source-commit(infinite)— Result of committing a scoped app's outgoing source-control changes via commitAppSource — the execution-tracker id and final state (success/failure) of the server-side commit+push to the app's linked git remote.
attachment(infinite)— Metadata for one sys_attachment row — file name, content type, size, and the record (table_name + table_sys_id) it hangs off. Written by attachmentList and attachmentUpload.

Files

attachment-file(application/octet-stream)— The raw bytes of a ServiceNow attachment fetched by attachmentDownload, tagged with its sys_id, file name, and parent record.
04Previous Versions9
2026.10.07.1

Adds Attachment API methods (attachmentList, attachmentDownload, attachmentUpload, attachmentDelete). Uploads are confined to regular files inside the repo (no .swamp/, vaults/ or .git paths, symlink-safe); downloads are stored separately from attachment metadata. Deletes decide 'already gone' by HTTP 404 status, never by text. List filters cannot escape their record scope. Public docs and examples use generic names.

Modified 1 behaviors

2026.07.24.2

Fix: unwrap reference-shaped sys_id when querying with displayValue=all. Under displayValue=all the Table API returns every field as {value, display_value} — including sys_id — so the previous string cast yielded an empty id and rows fell back to positional names. Resource names are stable again for displayValue=all queries.

2026.07.24.1
2026.07.23.1

Modified 1 behaviors

2026.07.21.1

2026.07.21.1: provisionCustomerDashboard fan-out — assembles a complete responsive dashboard (Platform Analytics canvas) from provisioned sys_report records via the Table API (pa_dashboards/pa_tabs/sys_grid_canvas/sys_portal + RenderReport prefs + panes), resolving widgets by report title; deletes the auto-spawned New Tab 1 chain. New dashboard-canvas resource records every created sys_id for verification/rollback.

Modified 1 behaviors

2026.07.20.1

Multitenant per-connection config with org/company discriminator and tier2 scaling (previously merged as 2026.07.15.1 but not registry-published); README and source formatting alignment.

Modified 1 behaviors

2026.06.29.1

Re-sync GitHub source (setupOAuthProvider/addOAuthAccount + connection-alias minting); add README install section and document the OAuth provisioning methods.

Added 1, removed 1 behaviors

2026.05.27.1

Docs: backtick the model type in module JSDoc (symbols-docs scoring); standardize LICENSE. No functional change.

2026.05.05.3

Initial publish — ServiceNow Table API CRUD integration with OAuth client_credentials auth.

05Stats
A
93 / 100
Downloads
34
Archive size
47.5 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License1/2partial

    no SPDX license declared

  • Verified public repository2/2earned
06Platforms
07Labels