Introduction

snow-cli is a cross-platform command-line interface for working with ServiceNow instances.

It is designed for:

  • developers who need quick access to ServiceNow APIs,
  • automation scripts and CI jobs,
  • coding agents and LLM workflows,
  • teams that prefer machine-readable JSON, JSON Lines, TOON, or CSV output.

The binary is written in Rust and is intended to ship as a single executable with no runtime dependencies.

What you can do

With snow-cli, you can:

  • manage connection profiles,
  • log in using supported authentication methods,
  • query, create, update, and delete Table API records,
  • inspect table schemas,
  • call raw REST API endpoints,
  • run background scripts,
  • search ServiceNow code,
  • move data through export/import and seed workflows,
  • generate shell completions.

Command style

Commands use a noun-verb structure:

snow-cli <noun> <verb> [options]

Examples:

snow-cli profile add dev --instance https://example.service-now.com --auth-method basic --username admin
snow-cli auth login
snow-cli table list incident --query 'active=true' --limit 20

Installation

Quick install

macOS and Linux

Copy the command below into your terminal. The script shows exactly what it will do before downloading anything:

curl -fsSL https://raw.githubusercontent.com/ewatch/snow-cli/main/scripts/install.sh | bash

You will see a plan like this and be asked to confirm:

Plan:
  Download: https://github.com/ewatch/snow-cli/releases/download/v0.4.0/snow-cli-aarch64-apple-darwin.tar.gz
  Release:  v0.4.0
  Install to: /Users/you/.local/bin
  Binaries: snow-cli, snow-cli-ro

Proceed? [Y/n]

Skip the confirmation

If you are running this in CI or prefer no prompts:

FORCE=1 curl -fsSL https://raw.githubusercontent.com/ewatch/snow-cli/main/scripts/install.sh | bash

Use a different directory

INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/ewatch/snow-cli/main/scripts/install.sh | bash

Windows

Open PowerShell and run:

irm https://raw.githubusercontent.com/ewatch/snow-cli/main/scripts/install.ps1 | iex

The script shows the same plan-and-confirm flow. To skip the prompt in automation:

$env:FORCE = "1"; irm https://raw.githubusercontent.com/ewatch/snow-cli/main/scripts/install.ps1 | iex

What the script does (in plain English)

  1. Detects your operating system and CPU architecture.
  2. Finds the latest release on GitHub.
  3. Downloads the matching archive (tar.gz for macOS/Linux, zip for Windows).
  4. Extracts it to a temporary folder.
  5. Copies snow-cli and snow-cli-ro into the install directory.
  6. Tells you if the directory is missing from your PATH and how to add it.

No registry changes, no admin rights required by default, and the archive is deleted automatically.

Manual install (pre-built binaries)

If you prefer to install by hand, download the archive for your platform from the GitHub releases page.

PlatformArchive
macOS Intelsnow-cli-x86_64-apple-darwin.tar.gz
macOS Apple Siliconsnow-cli-aarch64-apple-darwin.tar.gz
Linux x64snow-cli-x86_64-unknown-linux-gnu.tar.gz
Windows x64snow-cli-x86_64-pc-windows-msvc.zip

The "unknown" in the Linux filename is not a placeholder — it is the standard Rust target triple vendor field (x86_64-unknown-linux-gnu), which is how the release archives are named.

Extract the archive and place snow-cli (and optionally snow-cli-ro) in a directory on your PATH.

Build from source

Clone the repository and build the release binary:

git clone https://github.com/ewatch/snow-cli.git
cd snow-cli
cargo build --release

The binaries are created at:

target/release/snow-cli
target/release/snow-cli-ro

Run:

./target/release/snow-cli --help

Development build

For local development, run through Cargo:

cargo run -- --help
cargo run -- table list --help

Verify the project

Before contributing changes, run:

cargo fmt -- --check
cargo test
cargo clippy -- -D warnings

Configuration file

snow-cli stores profile configuration in:

~/.servicenow/config.toml

Secrets are stored in the operating system keychain where possible, not in plaintext config files.

Quick start

This page shows a minimal first-time workflow.

1. Create a profile

snow-cli profile add dev \
  --instance https://your-instance.service-now.com \
  --auth-method basic \
  --username your-user

You can also use the legacy alias:

snow-cli config add dev --instance https://your-instance.service-now.com --auth-method basic --username your-user

2. Store credentials

snow-cli auth login

For non-interactive use, pass a secret explicitly or through your automation environment:

snow-cli auth login --password '<password>'

3. Check authentication

snow-cli auth status

4. List records

snow-cli table list incident --query 'active=true' --limit 20

5. Get one record

snow-cli table get incident <sys_id>

6. Create a record

snow-cli table create incident --data '{"short_description":"Created from snow-cli"}'

You can also pipe JSON through stdin:

echo '{"short_description":"Created from stdin"}' | snow-cli table create incident

7. Choose output format

JSON is the default:

snow-cli table list incident --limit 5

Other supported formats include CSV, JSON Lines, TOON, and text where applicable:

snow-cli --output csv table list incident --fields number,short_description --limit 5
snow-cli --output jsonl table list incident --limit 5
snow-cli --output toon table list incident --limit 5

Testing with a Personal Developer Instance (PDI)

A ServiceNow Personal Developer Instance (PDI) is a free, full-featured instance you can use to test snow-cli safely without affecting production data.

Request a PDI

  1. Go to https://developer.servicenow.com/dev.do and sign in.
  2. Click Request Instance and choose a release family.
  3. Wait for the instance to be provisioned. You will receive:
    • Instance URL, e.g. https://dev123456.service-now.com
    • Username, usually admin
    • Password (displayed once; save it securely)

Quick test with basic auth

The fastest way to verify snow-cli against your PDI is basic authentication.

1. Create a profile

snow-cli profile add pdi \
  --instance https://dev123456.service-now.com \
  --auth-method basic \
  --username admin

Replace https://dev123456.service-now.com with your actual instance URL.

2. Log in

snow-cli auth login --profile pdi

When prompted, enter the admin password from your PDI request.

3. Verify

snow-cli auth status --profile pdi
snow-cli --profile pdi table list incident --limit 5

Testing read-only mode

snow-cli-ro is a read-only variant of the CLI. It is useful when you want to guarantee that no mutations happen on your PDI.

snow-cli-ro --profile pdi table list incident --limit 5

snow-cli-ro behaves exactly like snow-cli --read-only. Any command that would write, update, or delete data is blocked before it reaches the network.

Setting up OAuth on a PDI

If you want to test OAuth2 flows, you can create an OAuth application inside your PDI.

1. Create an OAuth application

  1. In your PDI, navigate to System OAuth > Application Registry.
  2. Click New and choose Create an OAuth API endpoint for external clients.
  3. Fill in:
    • Name: snow-cli
    • Redirect URL: http://127.0.0.1:8080/oauth/callback
    • Active: checked
  4. Click Submit.
  5. Open the newly created record and note:
    • Client ID
    • Client Secret (click the lock icon to reveal it)

2. Create an authorization-code profile

snow-cli profile add pdi-oauth \
  --instance https://dev123456.service-now.com \
  --auth-method oauth2 \
  --client-id YOUR_CLIENT_ID \
  --oauth-grant-type authorization-code

3. Log in

snow-cli auth login --profile pdi-oauth

The CLI opens a browser window. Sign in to your PDI and approve the OAuth request.

4. Verify

snow-cli --profile pdi-oauth table list incident --limit 5

PDI-specific tips

  • Wakeup: PDIs hibernate after a period of inactivity. The first request after wakeup may take 30–60 seconds. Increase the timeout if needed:
    snow-cli --profile pdi --timeout-secs 120 table list incident --limit 5
    
  • Instance release: PDIs are recreated on major release upgrades. Save your profile config and re-create the profile on a new instance if needed.
  • Data safety: PDIs are meant for experimentation. Feel free to create, update, and delete records. If something breaks, request a new instance.

Configuration and authentication

snow-cli keeps connection metadata in a config file and stores secrets in the operating system keychain where possible.

Where configuration lives

By default, profiles are stored in:

~/.servicenow/config.toml

The config file contains non-secret settings such as:

  • instance URL,
  • authentication method,
  • username,
  • OAuth client ID and grant settings,
  • mTLS certificate paths (not yet implemented),
  • default profile name.

Secrets such as passwords, API tokens, OAuth client secrets, and stored OAuth tokens are kept outside the TOML file.

Profiles

Profiles describe how snow-cli connects to a ServiceNow instance.

Create a basic-auth profile:

snow-cli profile add dev \
  --instance https://dev.service-now.com \
  --auth-method basic \
  --username admin

List configured profiles:

snow-cli profile list

Set the default profile:

snow-cli profile default dev

Show which profile is active:

snow-cli profile current
snow-cli profile show

Use a specific profile for one command:

snow-cli --profile dev table list incident --limit 10

For detailed profile management, see profile.

Authentication commands

Once a profile exists, store or refresh credentials with:

snow-cli auth login
snow-cli auth status
snow-cli auth token
snow-cli auth logout

For detailed auth behavior and secret input options, see auth.

Supported authentication methods

The CLI supports these --auth-method values:

  • basic
  • oauth2
  • api-key
  • browser-session

In config.toml, the API key method is serialized as api_key.

Basic authentication

Store username in the profile and the password with auth login:

snow-cli profile add dev \
  --instance https://dev.service-now.com \
  --auth-method basic \
  --username admin

snow-cli auth login --profile dev

OAuth 2.0

snow-cli supports three OAuth2 grant types:

  • client-credentials
  • password
  • authorization-code

Use authorization-code when you need the CLI to act in user scope. That flow uses a browser login, a localhost callback, and PKCE.

See the dedicated guide:

API key

Create a profile and store the token:

snow-cli profile add integration \
  --instance https://dev.service-now.com \
  --auth-method api-key

printf '%s' "$SNOW_API_TOKEN" | snow-cli auth login --profile integration --token-stdin

Browser session

browser-session profiles accept an already-authenticated Cookie header value from your browser. This is useful for instances that require SSO or SAML, where you can copy the cookie from an authenticated browser session.

Example profile:

snow-cli profile add sso-dev \
  --instance https://dev.service-now.com \
  --auth-method browser-session

Then provide the cookie at runtime via the SNOW_SESSION_COOKIE environment variable or the --session-cookie flag:

export SNOW_SESSION_COOKIE='JSESSIONID=...; glide_user_route=...'
snow-cli table list incident --limit 10 --profile sso-dev

The session cookie is never stored in the config file or keychain.

Global options

Common top-level flags:

snow-cli --profile dev <command>
snow-cli --instance https://override.service-now.com <command>
snow-cli --output json|csv|jsonl|toon|text <command>
snow-cli --timeout-secs 30 <command>
snow-cli -v <command>
snow-cli -vv <command>

Discover more

OAuth authorization code with PKCE

Use OAuth 2.0 authorization-code login when snow-cli must act in user scope instead of as a service account. This is the right setup when you want the CLI to operate as the signed-in human user and obtain a user-bound access token.

snow-cli implements this flow with:

  • the ServiceNow authorization endpoint,
  • a temporary localhost redirect listener,
  • PKCE (code_challenge_method=S256),
  • secure token storage in the OS keychain,
  • automatic token refresh when a refresh token is available.

What you need from ServiceNow

Before configuring the CLI, create an OAuth application in ServiceNow that supports the authorization-code flow.

Create the OAuth application in ServiceNow

  1. Navigate to System OAuth > Application Registry.
  2. Click New and choose Create an OAuth API endpoint for external clients.
  3. Fill in:
    • Name: snow-cli (or any descriptive name)
    • Redirect URL: http://127.0.0.1:8080/oauth/callback
    • Active: checked
  4. Click Submit.
  5. Open the newly created record and note:
    • Client ID
    • Client Secret (click the lock icon to reveal it)

PDI tip: If you are using a Personal Developer Instance, the steps are the same. See Testing with a PDI for a full walkthrough.

You will need:

  • the instance URL, for example https://dev123456.service-now.com
  • the OAuth client_id
  • an allowed redirect URI that matches the CLI profile
  • the scopes you want to request
  • optionally, a client_secret if you are using a confidential client

Default redirect URI

If you do not override the redirect settings, snow-cli uses:

http://127.0.0.1:8080/oauth/callback

That means the redirect URI configured in ServiceNow should usually be:

http://127.0.0.1:8080/oauth/callback

Allowed redirect hosts

For safety, the CLI only allows loopback redirect hosts. Use one of:

  • 127.0.0.1
  • localhost
  • ::1

Non-loopback hosts such as 0.0.0.0, LAN IPs, or public hostnames are rejected.

Step 1: create a profile

Create an OAuth2 profile that uses the authorization-code grant:

snow-cli profile add user-scope \
  --instance https://dev123456.service-now.com \
  --auth-method oauth2 \
  --client-id YOUR_CLIENT_ID \
  --oauth-grant-type authorization-code \
  --oauth-scope useraccount

Important profile options

  • --client-id: required for all OAuth2 flows
  • --oauth-grant-type authorization-code: enables browser login with PKCE
  • --oauth-scope: optional; defaults to useraccount
  • --oauth-redirect-host: optional; defaults to 127.0.0.1
  • --oauth-redirect-port: optional; defaults to 8080
  • --oauth-redirect-path: optional; defaults to /oauth/callback

If you need a different redirect URI, change the profile and update the same value in ServiceNow:

snow-cli profile edit user-scope \
  --oauth-redirect-host localhost \
  --oauth-redirect-port 8484 \
  --oauth-redirect-path /callback

That profile would require this redirect URI in ServiceNow:

http://localhost:8484/callback

Step 2: log in

Start the browser-based login:

snow-cli auth login --profile user-scope

What happens next:

  1. snow-cli starts a temporary local HTTP listener on the configured loopback redirect URI.
  2. It generates a PKCE code_verifier and code_challenge.
  3. It prints the authorization URL and tries to open it in your browser.
  4. You sign in to ServiceNow and approve the OAuth request.
  5. ServiceNow redirects back to the local callback URL.
  6. snow-cli exchanges the authorization code for tokens and stores them securely.

On success, the CLI prints a JSON summary including the redirect URI, requested scope, and whether a refresh token was returned.

Public PKCE client vs confidential client

Public PKCE client

If your ServiceNow OAuth client is configured as a public PKCE client, you can omit the client secret entirely:

snow-cli auth login --profile user-scope

This is the preferred setup for local user login flows.

Confidential client

If your ServiceNow OAuth client requires a secret, provide it during login:

printf '%s' "$SNOW_CLIENT_SECRET" | \
  snow-cli auth login --profile user-scope --client-secret-stdin

The secret is optional for authorization-code profiles, but if you provide it, snow-cli will also use it for token refresh requests.

Step 3: verify the session

Check auth status:

snow-cli auth status --profile user-scope

Print the current access token:

snow-cli auth token --profile user-scope

Use the profile in normal commands:

snow-cli --profile user-scope table list incident --limit 10

Headless or remote-browser workflows

If you do not want the CLI to open the browser automatically, use --no-browser:

snow-cli auth login --profile user-scope --no-browser

The CLI prints the authorization URL so you can copy it into a browser manually.

Example config.toml entry

After creating the profile, the config file contains non-secret metadata similar to:

default_profile = "user-scope"

[profiles.user-scope]
instance = "https://dev123456.service-now.com"
auth_method = "oauth2"
client_id = "YOUR_CLIENT_ID"
oauth_grant_type = "authorization_code"
oauth_scope = "useraccount"

Redirect settings appear only when you override the defaults.

Troubleshooting

Redirect URI mismatch

If ServiceNow says the redirect URI is invalid, make sure these values match exactly:

  • the redirect URI configured in ServiceNow
  • the snow-cli profile redirect host, port, and path
  • the actual URI printed by snow-cli auth login

Port already in use

If the CLI cannot bind the redirect listener, choose a different port:

snow-cli profile edit user-scope --oauth-redirect-port 8484

Then update the ServiceNow OAuth client redirect URI to the same port.

Login timed out

The CLI waits up to five minutes for the OAuth redirect. Re-run snow-cli auth login if the browser flow was interrupted.

Scope issues

If the token does not have the permissions you expect, review the requested OAuth scopes. useraccount is the default scope used by snow-cli when you do not specify --oauth-scope.

Accidentally reusing a confidential-client secret

For authorization-code profiles, if you omit --client-secret on a later login, snow-cli clears any previously stored secret for that profile. This helps public PKCE clients avoid sending a stale secret by accident.

Command reference

snow-cli uses a noun-verb command style:

snow-cli <noun> <verb> [options]

Examples:

snow-cli profile add dev --instance https://dev.service-now.com --auth-method basic --username admin
snow-cli auth login
snow-cli table list incident --query 'active=true' --limit 20

Use the pages in this section for command-specific guidance. The built-in help remains the authoritative source for exact usage:

snow-cli --help
snow-cli <noun> --help
snow-cli <noun> <verb> --help

Global flags

Every command supports these top-level flags:

  • --profile <name>: use a specific saved profile
  • --instance <url>: temporarily override the instance URL from the active profile
  • --output <json|csv|jsonl|toon|text>: choose the stdout format
  • --timeout-secs <seconds>: override the HTTP timeout for the current command
  • -v, -vv, -vvv: increase log verbosity on stderr

Command pages

CommandWhat it is for
profileCreate, edit, inspect, and switch connection profiles
authLog in, log out, inspect auth status, and print tokens
tableCRUD operations and schema inspection for ServiceNow tables
dataExport, validate, and import data artifacts
seedPlanned test-data workflows
scopeInspect scopes, export inventory, and move files between scopes
attachmentList, download, and upload attachments
import-setLoad records into staging tables
apiSend raw REST requests to arbitrary endpoints
scriptRun background scripts
codesearchSearch code and metadata on an instance
completionsGenerate shell completion scripts

Common patterns

Use a saved profile

snow-cli --profile prod table list incident --limit 10

Pipe JSON into commands that accept stdin

echo '{"short_description":"Created from stdin"}' | snow-cli table create incident
echo '{"user_name":"import-user"}' | snow-cli import-set load imp_user

Ask the CLI for help at the exact level you need

snow-cli auth --help
snow-cli auth login --help
snow-cli scope move-file --help

profile

Use profile to manage saved ServiceNow connection profiles.

snow-cli profile <verb> [options]

config remains available as a legacy alias for profile.

All profile subcommands also accept the global flags described in the command overview, although most profile-management tasks mainly use the command-specific flags below.

What a profile stores

A profile can contain:

  • the instance URL,
  • the auth method,
  • the username for basic auth or OAuth password grant,
  • OAuth client settings,
  • mTLS certificate paths,
  • SAML/SSO login URL.

Secrets are stored separately in the OS keychain.

profile add <name>

Create a new named profile.

snow-cli profile add <name> [options]

Important options:

  • --instance <url>: instance URL such as https://dev.service-now.com
  • --auth-method <basic|oauth2|api-key|mtls|saml>
  • --username <user>: used for basic auth or OAuth password grant
  • --client-id <id>: required for OAuth2 profiles
  • --oauth-grant-type <client-credentials|password|authorization-code>
  • --oauth-scope <scope>: optional for authorization-code profiles, default useraccount
  • --oauth-redirect-host <host>
  • --oauth-redirect-port <port>
  • --oauth-redirect-path <path>
  • --cert-path <path> and --key-path <path>: for mTLS profiles
  • --sso-login-url <url>: for SAML/SSO profiles

Examples:

snow-cli profile add dev \
  --instance https://dev.service-now.com \
  --auth-method basic \
  --username admin

snow-cli profile add user-scope \
  --instance https://dev.service-now.com \
  --auth-method oauth2 \
  --client-id abc123 \
  --oauth-grant-type authorization-code \
  --oauth-scope useraccount

snow-cli profile add mtls-dev \
  --instance https://dev.service-now.com \
  --auth-method mtls \
  --cert-path ./client.crt \
  --key-path ./client.key

Notes:

  • When you create the first profile, snow-cli makes it the default automatically.
  • If the profile already exists, use profile edit instead.
  • For authorization-code OAuth2, see the dedicated PKCE guide.

profile edit <name>

Update an existing profile.

snow-cli profile edit <name> [options]

You can change only the fields you want. Omitted fields keep their existing values.

Examples:

snow-cli profile edit dev --username new-admin
snow-cli profile edit user-scope --oauth-scope "useraccount email"
snow-cli profile edit saml-dev --sso-login-url https://dev.service-now.com/login_with_sso.do

profile list

List all configured profiles.

snow-cli profile list

This is useful for checking:

  • profile names,
  • instance URLs,
  • auth methods,
  • which profile is the default.

Example:

snow-cli --output csv profile list

profile find --instance <value>

Find profiles by instance name, host, or full URL.

snow-cli profile find --instance dev123456
snow-cli profile find --instance dev123456.service-now.com
snow-cli profile find --instance https://dev123456.service-now.com

Use this when you know the instance but not the saved profile name.

profile sdk

Interoperate with ServiceNow now-sdk aliases.

snow-cli profile sdk <verb> [options]

Subcommands:

  • profile sdk list: list saved now-sdk aliases
  • profile sdk import: copy one or more now-sdk aliases into snow-cli
  • profile sdk export: export a snow-cli profile into the now-sdk alias store

Examples:

snow-cli profile sdk list
snow-cli profile sdk import --alias dev
snow-cli profile sdk import --all
snow-cli profile sdk export prod --alias prod-sdk

Important options:

  • profile sdk import --alias <name>: import one alias
  • profile sdk import --all: import every alias
  • profile sdk import --set-default: set the imported profile as the snow-cli default when importing a single alias
  • profile sdk export <profile> --alias <name>: change the destination alias name
  • profile sdk export <profile> --set-default: make the exported alias the now-sdk default

Note: current interoperability is aimed at basic-auth profiles.

profile default <name>

Set the profile used when --profile is not provided.

snow-cli profile default dev

This updates the default_profile entry in the config file.

profile current

Show a small summary of the active profile.

snow-cli profile current

This is the quickest way to confirm which profile the CLI will use right now.

profile show

Show the active profile configuration in more detail.

snow-cli profile show

Compared with profile current, this also includes the config path and profile details.

profile remove <name>

Delete a saved profile.

snow-cli profile remove <name>

Important options:

  • --yes: required when deleting the current default profile
  • --new-default <name>: required replacement default when deleting the current default profile

Example:

snow-cli profile remove old-dev
snow-cli profile remove dev --yes --new-default prod

When a profile is removed, snow-cli also deletes stored credentials for that profile on a best-effort basis.

auth

Use auth to store, inspect, and clear credentials for the active profile.

snow-cli auth <verb> [options]

All auth subcommands also accept the global flags from the command overview.

auth login

Authenticate and store credentials in the OS keychain.

snow-cli auth login [options]

auth login behaves differently depending on the profile's auth_method.

Which credentials are needed?

Profile auth methodWhat must already be in the profileWhat auth login needs
basicusernamepassword
oauth2 + client-credentialsclient_idclient secret
oauth2 + passwordclient_id, usernameclient secret and password
oauth2 + authorization-codeclient_id, optional redirect/scope settingsbrowser login, optional client secret
api-keyno extra profile secret fieldsAPI token
browser-sessionno extra profile fieldssession cookie via env var or flag; auth login is not used

Secret input options

Prefer interactive prompts or --*-stdin flags over putting secrets directly on the command line.

Important options:

  • --password / --password-stdin: basic auth password or OAuth password-grant password
  • --token / --token-stdin: API token for API-key profiles
  • --client-secret / --client-secret-stdin: OAuth client secret
  • --session-cookie / --session-cookie-stdin: full authenticated Cookie header value for SAML profiles
  • --no-browser: print the OAuth authorization URL instead of trying to open it automatically
  • --also-now-sdk: for basic auth, also write the successful login into now-sdk
  • --now-sdk-alias <name>: destination alias name when using --also-now-sdk
  • --set-now-sdk-default: mark that now-sdk alias as default

Examples:

snow-cli auth login
printf '%s' "$SNOW_PASSWORD" | snow-cli auth login --password-stdin
printf '%s' "$SNOW_API_TOKEN" | snow-cli auth login --token-stdin
printf '%s' "$SNOW_CLIENT_SECRET" | snow-cli auth login --client-secret-stdin
printf '%s' "$SNOW_SESSION_COOKIE" | snow-cli auth login --session-cookie-stdin

OAuth authorization-code login

For authorization-code profiles, snow-cli:

  1. starts a temporary localhost callback listener,
  2. generates PKCE values,
  3. opens the authorization URL or prints it when --no-browser is used,
  4. waits for the redirect,
  5. exchanges the code for tokens,
  6. stores the resulting OAuth token set securely.

Public PKCE clients can omit the client secret. Confidential clients can provide one.

See OAuth authorization code with PKCE.

Browser session

For browser-session profiles, provide the full authenticated Cookie header value via the SNOW_SESSION_COOKIE environment variable or the --session-cookie / --session-cookie-stdin flags. This auth method does not use auth login.

auth status

Show the current authentication state for the active profile.

snow-cli auth status

Use it to confirm whether the required credentials are available.

auth token

Print the current access token to stdout.

snow-cli auth token

This is useful for piping into other tools. For OAuth2 profiles, the command prints an actual short-lived access token rather than a stored client secret.

Example:

snow-cli auth token | pbcopy

auth logout

Remove stored credentials for the active profile.

snow-cli auth logout

This clears the credential entries used by the current auth method.

Common workflows

Basic auth

snow-cli profile add dev \
  --instance https://dev.service-now.com \
  --auth-method basic \
  --username admin

snow-cli auth login --profile dev

OAuth2 client credentials

snow-cli profile add integration \
  --instance https://dev.service-now.com \
  --auth-method oauth2 \
  --client-id abc123 \
  --oauth-grant-type client-credentials

printf '%s' "$SNOW_CLIENT_SECRET" | \
  snow-cli auth login --profile integration --client-secret-stdin

OAuth2 authorization code with PKCE

snow-cli profile add user-scope \
  --instance https://dev.service-now.com \
  --auth-method oauth2 \
  --client-id abc123 \
  --oauth-grant-type authorization-code

snow-cli auth login --profile user-scope

table

Use table for generic CRUD operations against ServiceNow tables.

snow-cli table <verb> [options]

All table subcommands also accept the global flags from the command overview.

table list <table>

List records from a table.

snow-cli table list <table> [options]

Important options:

  • --query <encoded-query>: ServiceNow encoded query string
  • --fields <a,b,c>: comma-separated field list
  • --limit <n>: maximum number of records to return
  • --order-by <field>: sort by a field

Examples:

snow-cli table list incident --query 'active=true' --limit 20
snow-cli table list sys_user --fields sys_id,user_name,email --order-by user_name

Notes:

  • table list auto-paginates until it reaches the requested limit or exhausts the result set.
  • Use --output csv when you want tabular export.

table get <table> <sys_id>

Fetch a single record.

snow-cli table get <table> <sys_id> [options]

Important options:

  • --fields <a,b,c>: restrict the returned fields

Example:

snow-cli table get incident 46d44a4b2f13000044e0bfc8fb99b6fd --fields number,short_description,state

table create <table>

Create a new record.

snow-cli table create <table> --data '{"field":"value"}'

Important options:

  • --data <json>: JSON object to send to the Table API

If --data is omitted and stdin is piped in, the command reads JSON from stdin.

Examples:

snow-cli table create incident --data '{"short_description":"VPN down"}'
echo '{"short_description":"Created from stdin"}' | snow-cli table create incident

table update <table> <sys_id>

Patch an existing record.

snow-cli table update <table> <sys_id> --data '{"field":"value"}'

Important options:

  • --data <json>: JSON object with fields to change

If --data is omitted and stdin is piped in, the command reads JSON from stdin.

Example:

snow-cli table update incident 46d44a4b2f13000044e0bfc8fb99b6fd --data '{"state":"2"}'

table delete <table> <sys_id>

Delete a record.

snow-cli table delete <table> <sys_id> [--yes]

Important options:

  • --yes: skip the confirmation prompt

Notes:

  • In an interactive shell, the command asks for confirmation unless --yes is used.
  • In non-interactive environments, use --yes explicitly.

Example:

snow-cli table delete incident 46d44a4b2f13000044e0bfc8fb99b6fd --yes

table schema <table>

Inspect table columns using sys_dictionary.

snow-cli table schema <table> [options]

Important options:

  • --extended: include metadata such as required, read-only, max length, default, and reference table
  • --include-inherited: include fields inherited from parent tables

Examples:

snow-cli table schema incident
snow-cli table schema incident --extended
snow-cli table schema incident --extended --include-inherited

This is especially useful before building imports, exports, or scripted automation.

Common examples

snow-cli table list incident --query 'priority=1^active=true'
snow-cli table get sys_user <sys_id>
snow-cli table create cmdb_ci --data '{"name":"router-01"}'
snow-cli table update incident <sys_id> --data '{"assigned_to":"6816f79cc0a8016401c5a33be04be441"}'
snow-cli table delete incident <sys_id> --yes

data

Use data for portable export, validation, and import workflows that sit above the raw Table API.

snow-cli data <verb> [options]

All data subcommands also accept the global flags from the command overview.

data export <table>

Export records from a single table into a portable artifact.

snow-cli data export <table> [options]

Important options:

  • --query <encoded-query>: restrict exported rows
  • --fields <a,b,c>: only export selected fields
  • --limit <n>: maximum records to export
  • --order-by <field>: sort the export
  • -o, --out <path>: write to a file instead of stdout

Examples:

snow-cli data export incident --query 'active=true'
snow-cli data export incident --fields sys_id,number,short_description --limit 50
snow-cli --output csv data export sys_user --fields sys_id,user_name,email --out users.csv

Use this command when you want a flat, single-table artifact that can later be validated or re-imported.

data export-package --file <spec> --out-dir <dir>

Export a multi-table dataset package from a manifest spec.

snow-cli data export-package --file dataset-spec.json --out-dir exported-dataset

Important options:

  • -f, --file <path>: dataset export spec file
  • --out-dir <dir>: destination directory for the manifest and exported table files

Examples:

snow-cli data export-package --file dataset-spec.json --out-dir exported-dataset
snow-cli data validate --file exported-dataset/manifest.json
snow-cli data import --file exported-dataset/manifest.json

Note: data export-package does not support --output csv.

data validate --file <file>

Validate an export artifact or dataset package against the target instance.

snow-cli data validate --file export.json

Important options:

  • -f, --file <path>: file to validate

The validation report checks schema compatibility and reports errors and warnings before you try an import.

Note: data validate does not support --output csv.

data import --file <file>

Import a flat export artifact or dataset package.

snow-cli data import --file export.json [options]

Important options:

  • -f, --file <path>: file to import
  • --dry-run: preview the import plan without creating records
  • --import-set-table <table>: use the Import Set API for a flat table-export artifact
  • --fail-on-error: exit non-zero when Import Set responses contain row-level errors

Examples:

snow-cli data import --file users.json
snow-cli data import --file users.json --dry-run
snow-cli data import --file users.json --import-set-table imp_user
snow-cli data import --file users.json --import-set-table imp_user --fail-on-error

Notes:

  • --import-set-table currently works only for flat table-export artifacts, not multi-table package imports.
  • When --import-set-table is not used, the CLI falls back to direct create-only Table API writes.
  • --fail-on-error is mainly useful together with --import-set-table.
  • data import does not support --output csv.

When to use data instead of table

Use data when you want:

  • a portable export artifact,
  • schema validation before import,
  • a repeatable dataset package,
  • a dry-run preview of an import,
  • an Import Set-backed import path.

Use table for direct record-by-record CRUD operations.

seed

seed is reserved for declarative test-data workflows.

snow-cli seed <verb> [options]

All seed subcommands also accept the global flags from the command overview.

Current status

The command surface exists, but the implementation is not finished yet. At the moment, these commands return a "planned but not implemented yet" error.

seed plan --file <file>

Intended purpose: validate a seed spec and show the execution plan.

snow-cli seed plan --file qa-fixture.json

Important options:

  • -f, --file <path>: seed specification file

seed apply --file <file>

Intended purpose: create test data from a seed spec.

snow-cli seed apply --file qa-fixture.json

Important options:

  • -f, --file <path>: seed specification file

seed cleanup <run_id>

Intended purpose: remove data created by a prior seed run.

snow-cli seed cleanup <run_id> [options]

Important options:

  • --dry-run: preview what would be deleted
  • --yes: skip confirmation when deletion becomes available

Example:

snow-cli seed cleanup run-123 --dry-run

What to use today

Until seed is implemented, use:

  • data for export and import workflows
  • table for direct create/update/delete operations
  • import-set for staging-table loads

scope

Use scope to inspect ServiceNow application scope metadata and work with scope-related artifacts.

snow-cli scope <verb> [options]

All scope subcommands also accept the global flags from the command overview.

List scopes and classify them by origin.

snow-cli scope list
snow-cli scope list incident

Important options:

  • [search]: optional search term for partial matches or exact scope names
  • --kind <kind>: filter by one or more kinds
  • --show-source-table: include the source table column in text output
  • --show-sys-id: include the sys_id column in text output

Supported --kind values:

  • store-app
  • plugin
  • custom-app
  • platform
  • platform-app

Examples:

snow-cli scope list
snow-cli scope list sn_ot_incident_mgmt
snow-cli scope list incident --kind plugin --kind store-app

scope inspect <scope>

Inspect a scope and summarize its metadata.

snow-cli scope inspect <scope> [options]

<scope> can be either:

  • a scope name such as x_my_app, or
  • a scope sys_id.

Important options:

  • --details <basic|full>: choose whether to include the full artifact list

Examples:

snow-cli scope inspect x_my_app
snow-cli scope inspect 4f7f9bfe1b2a9010d9f2ed7c2e4bcb12 --details full

Use basic when you only need counts and summary data. Use full when you want normalized artifact rows in the response.

scope inventory <scope>

Export normalized artifact rows for a scope.

snow-cli scope inventory <scope>

This is useful for:

  • machine-readable analysis,
  • CSV export,
  • comparing application contents,
  • downstream scripting.

Example:

snow-cli --output csv scope inventory x_my_app

scope move-file <table> <sys_id> --target-scope <scope>

Move one application file into another custom scope without changing its sys_id.

snow-cli scope move-file <table> <sys_id> --target-scope <scope> [options]

Important options:

  • --target-scope <scope>: required target scope name or scope sys_id
  • --dry-run: validate and preview the move without persisting it
  • --yes: confirm execution when warnings are reported

Examples:

snow-cli scope move-file sys_script_include 4f7f9bfe1b2a9010d9f2ed7c2e4bcb12 \
  --target-scope x_target_app --dry-run

snow-cli scope move-file sys_script_include 4f7f9bfe1b2a9010d9f2ed7c2e4bcb12 \
  --target-scope x_target_app --yes

Notes:

  • scope move-file runs a background script behind the scenes.
  • --dry-run is the safest way to see warnings and proposed field changes before you commit the move.

attachment

Use attachment to work with files attached to ServiceNow records.

snow-cli attachment <verb> [options]

All attachment subcommands also accept the global flags from the command overview.

attachment list <table> <sys_id>

List attachments for a record.

snow-cli attachment list <table> <sys_id>

Arguments:

  • <table>: table name, for example incident
  • <sys_id>: record sys_id

Example:

snow-cli attachment list incident 46d44a4b2f13000044e0bfc8fb99b6fd

The result includes fields such as file name, content type, size, and download link.

attachment download <sys_id>

Download one attachment.

snow-cli attachment download <attachment_sys_id> [options]

Important options:

  • -o, --out <path>: write to a specific file path

Examples:

snow-cli attachment download 2d8b4c33db121010f4d224b5ca96198d
snow-cli attachment download 2d8b4c33db121010f4d224b5ca96198d --out incident-log.txt

If --out is omitted, the CLI tries to use the original attachment filename. If that is not safe or available, it falls back to <sys_id>.bin.

attachment upload <table> <sys_id> --file <path>

Upload a local file as an attachment.

snow-cli attachment upload <table> <sys_id> --file ./report.txt

Important options:

  • -f, --file <path>: local file to upload

Example:

snow-cli attachment upload incident 46d44a4b2f13000044e0bfc8fb99b6fd --file ./report.txt

Notes:

  • The CLI reads the file locally and uploads it with the attachment API.
  • Attachments larger than 100 MiB are rejected by the CLI before upload.

import-set

Use import-set to load records into staging tables through the Import Set API.

snow-cli import-set <verb> [options]

All import-set subcommands also accept the global flags from the command overview.

import-set load <table>

Post one JSON object into a staging table.

snow-cli import-set load <table> [options]

Important options:

  • --data <json>: JSON object to load
  • --fail-on-error: exit non-zero when the response contains row-level errors

Examples:

snow-cli import-set load imp_user --data '{"user_name":"snow-cli-user","email":"snow-cli-user@example.com"}'
echo '{"user_name":"stdin-user","email":"stdin-user@example.com"}' | snow-cli import-set load imp_user
snow-cli import-set load imp_user --fail-on-error --data '{"user_name":"ci-user","email":"ci-user@example.com"}'

Notes:

  • If --data is omitted and stdin is piped in, the command reads JSON from stdin.
  • The request is sent to /api/now/import/{table}.
  • The command prints a structured summary with counts for inserted, updated, ignored, error, and other result rows.
  • --fail-on-error is useful for CI or agent workflows where row-level failures must fail the command.

import-set transform <sys_id>

Reserved for separate transform execution.

snow-cli import-set transform <sys_id>

Current status:

  • the command surface exists,
  • the implementation is still a placeholder,
  • some instances already run the transform automatically during import-set load.

In other words, import-set load is the working path today.

api

Use api when you want to call a ServiceNow endpoint directly instead of using a higher-level command.

snow-cli api <verb> [options]

All api subcommands also accept the global flags from the command overview.

Supported verbs

  • api get <path>
  • api post <path>
  • api put <path>
  • api delete <path>

<path> should be a ServiceNow-relative path such as:

/api/now/table/incident?sysparm_limit=1
/api/x_myapp/status

Common options

  • -H, --header 'Key: Value': add a custom header; repeat the flag to send multiple headers
  • --data <body>: request body for post and put

Examples:

snow-cli api get /api/now/table/incident?sysparm_limit=1
snow-cli api post /api/x_myapp/action --data '{"dry_run":true}'
snow-cli api put /api/x_myapp/config --data '{"enabled":true}'
snow-cli api delete /api/x_myapp/config/abc123
snow-cli api get /api/x_myapp/status -H 'X-Trace-Id: abc123'

api get <path>

Send a GET request.

snow-cli api get <path> [-H 'Key: Value']

Use this for read-only endpoints.

api post <path>

Send a POST request.

snow-cli api post <path> [--data '{"key":"value"}']

If --data is omitted and stdin is piped in, the CLI reads the request body from stdin.

Example:

echo '{"dry_run":true}' | snow-cli api post /api/x_myapp/action

api put <path>

Send a PUT request.

snow-cli api put <path> [--data '{"key":"value"}']

Like post, this can also read the body from stdin.

api delete <path>

Send a DELETE request.

snow-cli api delete <path>

Use -H if the endpoint needs additional headers.

Response handling

snow-cli prints the raw response body to stdout.

  • If the response is valid JSON and you use JSON-like output, the CLI pretty-prints it.
  • If the response is not valid JSON, the body is printed as-is.
  • --output csv prints the raw body unchanged.

This makes api the most flexible command when you need an endpoint that does not yet have a dedicated command.

script

Use script to execute a ServiceNow background script.

snow-cli script run [options]

All script subcommands also accept the global flags from the command overview.

script run

Execute a background script on the target instance.

snow-cli script run [options]

How to provide the script

You can provide script content in three ways:

  • --code <script>: inline code
  • --file <path>: read from a local file
  • stdin: pipe the script into the command

Precedence is:

  1. --code
  2. --file
  3. stdin

Examples:

snow-cli script run --code 'gs.info("hello from snow-cli")'
snow-cli script run --file ./cleanup.js
printf '%s' 'gs.info("from stdin")' | snow-cli script run

Important options

  • -c, --code <script>: inline script text
  • -f, --file <path>: local script file
  • --scope <scope>: scope to run in, default global
  • --endpoint <path>: execution endpoint, default /sys.scripts.do
  • --rollback: record rollback context for database changes
  • --sandbox: prevent database writes
  • --scriptlet: run as a scriptlet with access to global server-side objects
  • --quota-managed-transaction: use managed transaction limits for long-running scripts

Examples:

snow-cli script run --code 'gs.info("hello")' --scope x_my_app
snow-cli script run --file ./job.js --sandbox
snow-cli script run --code 'gs.sleep(1000); gs.info("done")' --quota-managed-transaction

Hints from live E2E testing

  • For safe live verification on a real instance, start with --sandbox so you can confirm auth, form bootstrap, and script execution without writing records.
  • All three input modes were validated end to end: --code, --file, and stdin.
  • Multiline scripts also worked from both --file and stdin.
  • Some ServiceNow instances use older background script JavaScript parsing. If the instance reports an older script engine level (for example Script ES Level: 0), wrapper syntax such as IIFEs may fail with errors like Invalid function definition even though simpler multiline scripts work.
  • If you hit parser compatibility issues, prefer plain top-level statements over wrapper patterns.

Example multiline stdin run:

cat <<'EOF' | snow-cli script run --sandbox
var user = gs.getUserName();
gs.print('start');
gs.print('user=' + user);
gs.print('end');
EOF

When to use script

Use this command when you need to:

  • run a quick background script,
  • inspect data from server-side APIs,
  • perform one-off maintenance,
  • prototype logic before turning it into an app artifact.

For raw REST endpoints instead of background scripts, use api.

codesearch

Use codesearch to search code and related metadata on a ServiceNow instance.

snow-cli codesearch search <query> [options]

All codesearch subcommands also accept the global flags from the command overview.

codesearch search <query>

Search instance code for a text query.

snow-cli codesearch search <query> [options]

Important options:

  • --source-table <table>: limit results to a specific source table such as sys_script_include or sys_script
  • --limit <n>: maximum number of results, default 100
  • --current-scope: search only in the current scope
  • --search-group <name>: advanced search-group override

Examples:

snow-cli codesearch search GlideRecord
snow-cli codesearch search GlideRecord --source-table sys_script_include
snow-cli codesearch search gs.info --current-scope
snow-cli codesearch search BusinessRule --limit 250

Notes:

  • The default search group is sn_devstudio.Studio Search Group.
  • Depending on the instance response shape, the CLI may print normalized records or pretty-printed JSON.

completions

Use completions to generate shell completion scripts.

snow-cli completions <shell>

Supported shells:

  • bash
  • zsh
  • fish
  • powershell
  • elvish

Examples

Print a completion script to stdout:

snow-cli completions zsh

Save it to a file:

snow-cli completions bash > snow-cli.bash
snow-cli completions zsh > _snow-cli
snow-cli completions fish > snow-cli.fish

Typical installation patterns:

# bash
snow-cli completions bash > ~/.local/share/bash-completion/completions/snow-cli

# zsh
snow-cli completions zsh > ~/.zfunc/_snow-cli

# fish
snow-cli completions fish > ~/.config/fish/completions/snow-cli.fish

Notes

  • The command writes the generated completion script to stdout.
  • Unlike most commands, the useful output here is the shell script itself, not structured JSON.
  • Re-run the command after upgrading the CLI if the command surface changed.

Deploying this book to GitHub Pages

This repository includes an mdBook setup for publishing the documentation as a static GitHub Pages site.

Local preview

Install mdBook:

cargo install mdbook --locked

Serve the book locally:

mdbook serve

Open the local URL printed by mdBook, usually:

http://localhost:3000

Build the static site:

mdbook build

The generated HTML is written to:

book/

The book/ directory is generated output and should not be committed.

GitHub Pages setup

The workflow in .github/workflows/pages.yml builds and deploys the book with GitHub Actions.

In GitHub, configure Pages like this:

Repository → Settings → Pages → Build and deployment → Source → GitHub Actions

Then push changes to main, or run the workflow manually from the Actions tab.

Taking the page down

To unpublish the site quickly:

  1. Go to Repository → Settings → Pages.
  2. Disable Pages or change the source away from the Pages workflow.
  3. Optionally disable or delete .github/workflows/pages.yml.

Making the repository private usually removes public access shortly after GitHub updates Pages visibility, but disabling Pages is the most explicit option.

Project pages URL

For a repository named snow-cli, GitHub Pages typically publishes to:

https://<owner>.github.io/snow-cli/

If the repository name changes, update site-url in book.toml.

Further reading

The repository contains additional Markdown documentation for contributors and maintainers.

Project plan

Architecture decisions

Design notes

Contributor guides

Backlog

Task tracking is managed with bd / beads. The historical phase backlog remains in: