Skip to main content
Version: Latest (4.7.0)

CI Integration

Guide for running the Now SDK in CI/CD pipelines: non-interactive authentication for now-sdk install, validating that keys.ts is committed via now-sdk build --frozenKeys, and the conventions that keep pipeline runs reproducible.

When to Use

  • CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, etc.) building or installing a Fluent app.
  • Container builds, scheduled jobs, or scripted environments where interactive prompts aren't possible.
  • Pre-merge checks that need to fail fast when developers forget to commit generated artifacts.

Builds: --frozenKeys

now-sdk build regenerates src/fluent/generated/keys.ts (the registry mapping Now.ID['...'] identifiers to ServiceNow sys_ids) every time it runs. When a developer adds new Fluent records, the build appends a new entry to src/fluent/generated/keys.ts. That generated file must be committed to source control along with the Fluent code that introduced the identifier — see the keys.ts guide.

--frozenKeys is the CI-side enforcement of that rule. This will ensure that keys was not modified by the build, and if any change a build error will occur.

Usage

now-sdk build --frozenKeys

After building, the CLI compares keys.ts content before and after. If anything changed during the build, it fails with:

Keys file is out-of-date. To update it, run the build again without frozen keys.

This means a developer pushed Fluent code that introduces a new or modified identifier (or modifies a coalesce key) without checking in the updated keys.ts.

Why this matters

keys.ts is the source of truth for record identity. If a pipeline builds without it being committed:

  • Deploys produce different sys_ids on every machine. The same Now.ID['my-rule'] resolves to a freshly-generated sys_id on each developer/CI machine, so the same logical record ends up with different IDs across environments.
  • Updates become inserts. ServiceNow uses sys_id for record identity. A mismatch means the install creates a duplicate record instead of updating the existing one — corrupting the target instance.
  • Subsequent merges hide the problem. Once a stale keys.ts lands in main, every later branch picks up the wrong IDs.

Running --frozenKeys in CI catches this before merge. It's the same build the developer should have run locally, with a guard that errors instead of silently rewriting the file.

Run now-sdk build --frozenKeys as a pre-merge / pull-request check, before the install step:

# GitHub Actions example
- name: Verify keys.ts is up to date
run: now-sdk build --frozenKeys

If the keys check fails, the developer's fix is simple: run now-sdk build locally, commit the updated keys.ts, and push.


Authentication for now-sdk install

Non-interactive authentication for now-sdk install. The CLI reads credentials from environment variables instead of the local keychain, so it works in CI/CD pipelines and other headless environments. Two auth types are supported: basic (username + password) and oauth (OAuth 2.0 client_credentials grant).

When to Run now-sdk install from CI

Use this for a main-branch pipeline that deploys changes to the next-stage instance (typically a shared test / integration / dev instance). The pipeline installs the just-built app to that instance so QA, integration tests, and downstream environments can pick up the latest changes automatically after merge.

Do not use now-sdk install from CI to deploy to production instances. Production deploys should go through ServiceNow's standard application promotion mechanisms — the App Repo, or guided application install/upgrade — so that change management, approvals, and rollback work the way the platform expects.

Enabling CI Mode

CI mode is gated by a single env var:

export SN_SDK_NODE_ENV=SN_SDK_CI_INSTALL

When set, the CLI reads credentials from env vars instead of the keychain. Choose the auth type with SN_SDK_AUTH_TYPE:

SN_SDK_AUTH_TYPEBehavior
unset or basicUse SN_SDK_USER + SN_SDK_USER_PWD (basic auth). Default for backward compatibility.
oauthUse SN_SDK_OAUTH_CLIENT_ID + SN_SDK_OAUTH_CLIENT_SECRET (OAuth client_credentials).

Any other value is rejected with an error before any network call.

Basic Auth

Simplest setup — username and password of an instance user.

Environment Variables

VariableRequiredValue
SN_SDK_NODE_ENVyesSN_SDK_CI_INSTALL
SN_SDK_AUTH_TYPEnobasic (or unset)
SN_SDK_INSTANCE_URLyesFull instance URL, e.g. https://your-instance.service-now.com
SN_SDK_USERyesUsername
SN_SDK_USER_PWDyesPassword

Example

export SN_SDK_NODE_ENV=SN_SDK_CI_INSTALL
export SN_SDK_AUTH_TYPE=basic
export SN_SDK_INSTANCE_URL=https://your-instance.service-now.com
export SN_SDK_USER=ci-user
export SN_SDK_USER_PWD=...

now-sdk install

When to Choose Basic

  • PDIs, sandbox instances, fast iteration where OAuth setup overhead isn't worth it.
  • Existing pipelines already wired for username/password.

Avoid for production CI: every run sends the password to the instance, and rotating it means rotating in every pipeline secret store.

OAuth Client Credentials

Token-based, no user password ever leaves your secret store. The CLI fetches a fresh access token at the start of each run.

Environment Variables

VariableRequiredValue
SN_SDK_NODE_ENVyesSN_SDK_CI_INSTALL
SN_SDK_AUTH_TYPEyesoauth
SN_SDK_INSTANCE_URLyesFull instance URL
SN_SDK_OAUTH_CLIENT_IDyesOAuth Application Registry client_id
SN_SDK_OAUTH_CLIENT_SECRETyesOAuth Application Registry client_secret

Example

export SN_SDK_NODE_ENV=SN_SDK_CI_INSTALL
export SN_SDK_AUTH_TYPE=oauth
export SN_SDK_INSTANCE_URL=https://your-instance.service-now.com
export SN_SDK_OAUTH_CLIENT_ID=...
export SN_SDK_OAUTH_CLIENT_SECRET=...

now-sdk install

The CLI calls ${SN_SDK_INSTANCE_URL}/oauth_token.do once at startup with grant_type=client_credentials, then uses the returned access token for all subsequent requests. There is no refresh token in this grant — each invocation fetches a fresh token.

ServiceNow Instance Configuration

OAuth requires one-time setup on the instance. Complete all four steps or the token endpoint will reject the request.

1. Create or open the OAuth Application Registry

System OAuth → Application Registry → NewCreate an OAuth API endpoint for external clients. Do not use OIDC providers — they don't issue tokens for this grant.

  • Set "Public Client" to false
  • Set "OAuth Application User" to a sys_user in step 3

Note the Client ID and Client Secret — these become SN_SDK_OAUTH_CLIENT_ID and SN_SDK_OAUTH_CLIENT_SECRET.

2. Enable the client_credentials grant

Two things must be in place:

  • On the Application Registry record, Grant type must include Client Credentials.

  • System property glide.oauth.inbound.client.credential.grant_type.enabled must exist and be set to true. If the property does not already exist on the instance, create it in sys_properties with:

    • Name: glide.oauth.inbound.client.credential.grant_type.enabled
    • Type: true | false
    • Value: true

    See ServiceNow KB1645212 for details: https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB1645212

Without the system property set to true, the token endpoint will reject grant_type=client_credentials regardless of how the OAuth app is configured.

3. Configure the Service User

The user mapped in step 1 must:

  • Have roles sufficient to install your app — typically admin.
  • Have Identity Type = Human on the sys_user record. The SDK's OAuth flow performs a CSRF dance against /angular.do?sysparm_type=get_user to obtain a UI session token; AngularProcessor blocks machine identities with User <name> is not allowed to access com.glide.ui.ng.AngularProcessor, even when the user has admin.

When to Choose OAuth

  • Production CI/CD where credential rotation, audit, and least-privilege matter.
  • Multi-pipeline environments where a single shared service account beats one password per pipeline.
  • Compliance regimes that prohibit storing user passwords in CI secret stores.

Choosing an Auth Type

BasicOAuth Client Credentials
Setup effortNone — uses existing userOne-time OAuth app + entity profile + service user setup
Credential rotationRotate user passwordRotate client secret (no password change)
Audit trailUser attributionService-account attribution (via mapped Default User)
Network surfacePassword sent on every requestPassword never sent; token fetched once per run
Best forPDIs, sandbox, fast iterationProduction CI, regulated environments

Troubleshooting

SymptomLikely Cause
Keys file is out-of-date. To update it, run the build again without frozen keys.Developer added or changed an identifier without committing the regenerated keys.ts. Run now-sdk build locally, commit the file, push.
Unsupported value for SN_SDK_AUTH_TYPETypo. Must be exactly basic or oauth.
CI basic auth is missing required environment variables: ...One of SN_SDK_INSTANCE_URL / SN_SDK_USER / SN_SDK_USER_PWD is unset.
CI OAuth client_credentials is missing required environment variables: ...One of SN_SDK_INSTANCE_URL / SN_SDK_OAUTH_CLIENT_ID / SN_SDK_OAUTH_CLIENT_SECRET is unset.
401 {"error":"server_error","error_description":"access_denied"} from /oauth_token.doMissing Entity Profile + Default User on the OAuth app, client_credentials grant not enabled, or glide.oauth.inbound.client.credential.grant_type.enabled not set to true.
User <name> is not allowed to access com.glide.ui.ng.AngularProcessor after token is issuedMapped service user has Identity Type = Machine. Change to Human on the sys_user record.
401 invalid_clientClient ID or secret typo. Re-copy from the Application Registry record.
401 Unauthorized on basic authUsername/password incorrect, or the user lacks roles for the operation.

Check System Logs → All filtered to Source = OAuth (for OAuth) or Source = Transaction (for basic) for the precise platform-side error — response bodies are deliberately vague but the logs name the actual cause.

Security Notes

  • Never commit credentials to source control. Use your CI provider's secret store (GitHub Actions secrets, GitLab CI variables, Jenkins Credentials, etc.).
  • Use a dedicated service user rather than reusing a person's account — auditing, rotation, and role narrowing are all easier.
  • For OAuth, rotate the client secret regularly. The client_credentials grant has no refresh token, so each CLI run fetches a fresh access token from the secret.
  • For basic, rotate the password regularly and prefer a service account with the minimum roles required for the operations performed.