---
name: servicenow-oauth-app
description: >-
  Produces working ServiceNow OAuth 2.0 credentials — an OAuth application
  registry entry, redirect URL, client ID and client secret — for a platform
  that connects many customers' ServiceNow instances. Covers the fact that
  decides the whole shape of the task: the registry lives inside each
  customer's own instance, so every customer generates their own client ID and
  secret and there is no central or shared ServiceNow OAuth client to obtain.
  Use when asked to get ServiceNow OAuth credentials, create a ServiceNow
  OAuth API endpoint for external clients, write the admin runbook a customer
  follows to produce them, pick the instance subdomain a connection needs,
  choose the role for a ServiceNow integration user, set up a Personal
  Developer Instance for testing, rotate a ServiceNow client secret, or debug
  a ServiceNow authorization that succeeds and then returns fewer records than
  the table holds. For any other vendor's developer portal, use that vendor's
  skill instead.
---

# ServiceNow OAuth2 App Registration

Get working ServiceNow OAuth 2.0 credentials — the registry entry, the redirect URL, the client ID and the client
secret — for a platform that connects *other organizations'* ServiceNow instances.

**Read this before anything else: there is nothing for you to register.** ServiceNow's OAuth application registry
lives inside each ServiceNow instance. A registry entry created on `acme.service-now.com` exists only on
`acme.service-now.com`; its client ID is auto-generated by that instance and is read-only on the form, so it cannot be
made to match anyone else's. ServiceNow publishes no global, partner or Store-distributed OAuth client, and no
developer portal where you sign up an app once. **Every customer produces their own client ID and client secret inside
their own instance, and hands both to you.** The deliverable for a run of this skill is therefore usually not a
credential pair at all — it is (a) a short admin runbook a customer's ServiceNow admin can follow in about ten
minutes, and (b) a credential pair for *your own* test instance so you can prove the flow. §1 is the section that
decides everything else.

Three more things are expensive to get wrong. **The instance subdomain is mandatory and undiscoverable** — it is part
of the authorize URL, the token URL and the API base, so it must be collected from the customer *before* the browser
is redirected anywhere (§3). **ServiceNow has no requestable scope catalogue**: a token acts as the human who
authorized it, and what it can read is decided by that user's roles and ACLs, so least privilege means putting a
narrow role on a dedicated integration user, not asking for narrow scopes (§6). And **token lifetimes are fields on
the customer's registry record** — 30 minutes and 100 days by default, editable by their admin — so nothing in the
connector may assume a fixed lifetime (§8).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Whose instance** | Your own test instance, or a named customer's production instance — they are different runs (§1, §9) |
| **Instance subdomain** | The bare instance name from `https://<instance>.service-now.com`. Required for every URL (§3) |
| **Who holds `oauth_admin`** | Only an admin of that instance can create the entry. Usually not you (§2) |
| **Application name** and optional logo URL | Shown on the instance's authorization page (§4) |
| **Which redirect URL** | The one matching the data centre the customer's workspace lives in (§5) |
| **Which integration user authorizes** | The identity the token acts as, and therefore the whole access model (§6) |
| **Which objects the connector reads/writes** | Drives the role and ACL conversation with their admin (§6) |
| **Any REST API Auth Scope restrictions in force** | An instance-side allow-list that can silently exclude your calls (§6) |
| **Release family the instance is on** | Changes the UI path and whether the Machine Identity Console exists (§4, Platform state) |

## Quick Start

1. Establish that credentials are **per customer instance** and that no central client exists — then decide whether
   this run is a customer runbook, your own test credentials, or both (§1).
2. Confirm the instance prerequisites: OAuth plugin active, activation property true, an admin with `oauth_admin` (§2).
3. Collect the **instance subdomain** in the exact form the connector expects, before any redirect (§3).
4. Create the entry: **All → System OAuth → Application Registry → New → Create an OAuth API endpoint for external
   clients** (§4).
5. Put the correct **redirect URL** in, exactly, with no trailing slash (§5).
6. Settle access the ServiceNow way — a dedicated integration user with a narrow role, not a scope string (§6).
7. Record the **client ID** and **client secret** at creation; treat the secret as write-once (§7).
8. Check **Access Token Lifespan** and **Refresh Token Lifespan** on the record, and note that the customer can change
   both (§8).
9. Test on a **Personal Developer Instance**, knowing it hibernates and can be reclaimed (§9).
10. Verify end-to-end against a second, unrelated instance and a non-admin user (§10), then hand off (§11).

## Platform state (verified 2026-09-20 — re-verify before trusting)

Verified against the **Australia** release family documentation (the current family; its release notes page was
updated 10 September 2026). ServiceNow ships two feature families a year and the OAuth surface has moved twice
recently, so check which family the customer's instance is on before quoting a UI path.

- **Documentation moved.** `docs.servicenow.com/bundle/<release>-…` URLs now 301-redirect to
  `www.servicenow.com/docs/r/…`, and the redirect lands on the *current* release rather than the one named in the old
  URL. Links written against Washington DC or Xanadu still resolve but no longer pin a version.
- **The classic path still works**: **System OAuth → Application Registry → New → "Create an OAuth API endpoint for
  external clients"**, role `oauth_admin`.
- **A newer path exists: the Machine Identity Console**, for managing the service accounts and credentials behind
  inbound API integrations. Its docs are dated to the Australia release and it is gated behind the Machine Identity
  Management plugin (`com.glide.identity.machine_identity_management`). It offers a guided per-grant-type form for
  inbound integrations, including the authorization code grant. Either path produces the same thing — an entry in the
  instance's OAuth registry.
- **Documented defaults on the registry record**: Access Token Lifespan **1,800 seconds (30 minutes)**, Refresh Token
  Lifespan **8,640,000 seconds (100 days)**. Both are editable fields, so both are per-customer facts, not constants.
- **Client ID is auto-generated and read-only**; the client secret is auto-generated if the field is left blank.
- **Prerequisites**: the **OAuth 2.0** plugin (`com.snc.platform.security.oauth`) must be active and the property
  **`com.snc.platform.security.oauth.is.active`** must be `true`.
- **Documented grant types for inbound OAuth**: authorization code, client credentials, JWT bearer, third-party ID
  token (OIDC), resource owner password credentials, and implicit (described as legacy). The authorization code grant
  is documented as supporting **both confidential clients (client secret) and public clients (PKCE)**.
- **Scopes are an instance-side allow-list, not a request-time catalogue.** `useraccount` is the default scope and
  functions as an override — an entity carrying it "can access any API even if you have created a REST API Auth Scope
  record with a different auth scope." REST API Auth Scope records are created by the customer's admin
  (roles `api_service_admin`, `adaptive_auth_policy_admin`).
- **Rate limits are per-instance rules**, in `sys_rate_limit_rules`, created by a `rate_limit_admin` as a **request
  limit per hour** applied to one user, a role, or everyone. No out-of-the-box limit is documented. Over the limit
  returns **429** with a **`Retry-After`** header in seconds.
- **Personal Developer Instances hibernate**, and ServiceNow emails when one has been inactive for over 10 days;
  a reclaimed PDI "is reset to its original state and reassigned to another Developer Program member." Signing in to
  the Developer Site at least weekly keeps it.

If the instance does not look like this, stop and report what you actually see rather than clicking on.

## 1. There is no central ServiceNow OAuth client — decide what this run produces

This is the whole task. ServiceNow is not a vendor you register with; it is software each customer runs an instance
of, and each instance is its own OAuth authorization server.

| | What ServiceNow does | Consequence for a multi-tenant connector |
| --- | --- | --- |
| Where the app record lives | In the customer's instance, `System OAuth → Application Registry` | You cannot create it. Their admin must |
| Who owns the client ID | The instance generates it; the field is read-only | No shared identifier is possible, even in principle |
| Who owns the secret | The instance | You store N secrets, one per customer |
| Who authorizes | A user *on that instance* | Access follows that user's roles and ACLs (§6) |
| Central listing / marketplace client | None documented for inbound OAuth | A Store listing does not get you a shared client |

So a run of this skill produces one of three things, and you should say which before doing anything:

1. **A customer runbook** — the ordered click path, the literal redirect URL, the role guidance, and a safe way for
   them to return the client ID and secret. This is the normal output, and it is what to write when the person asking
   does not have admin on the instance in question.
2. **Your own test credentials** — a registry entry on a Personal Developer Instance (§9), used to prove the flow and
   to keep a regression environment. Legitimate, and frequently the only thing you can actually create yourself.
3. **A diagnosis** — an existing customer connection is failing and you need to work out whether it is their registry
   entry, their integration user's roles, an auth scope, or the connector (§10).

**Do not ask a customer for a ServiceNow username and password as a shortcut.** Basic authentication against the REST
API exists and some connectors support it, but it hands you a reusable human credential with that person's full
access, cannot be revoked without changing their password, and is the thing customer security reviews object to.
Client credentials grant is also available but binds the token to a fixed OAuth Application User with no user consent
and needs its own property enabled (`glide.oauth.inbound.client.credential.grant_type.enabled`) — it is a different
integration shape, not a drop-in for a customer-authorizes-us flow. If either is being proposed, raise it rather than
implementing it quietly.

> **Product fact — as of 2026-09-20, this platform's ServiceNow connector** is built for the per-instance model: it
> substitutes a customer-supplied instance host into the authorize URL, the token URL and the API base, and it also
> offers a username/password/subdomain alternative for customers who will not run the OAuth flow. It deliberately does
> **not** offer ServiceNow as a sign-in provider, precisely because an instance host has to be supplied before any
> redirect can be built. Confirm the current behaviour with the connector's owner before writing it into a runbook.

## 2. Prerequisites inside the instance, and the errors when they are missing

Before the form will do anything useful:

- **Plugin**: **OAuth 2.0**, `com.snc.platform.security.oauth`, must be **active**.
- **Property**: **`com.snc.platform.security.oauth.is.active`** must be `true`. The documentation ties this directly to
  whether "the instance can generate OAuth 2.0 tokens."
- **Role**: **`oauth_admin`** is the role the create-endpoint procedure names. Related pages name `admin` and
  `security_admin` for adjacent tasks, and the inbound OAuth overview lists `oauth_admin`, `mi_admin` or `admin` as
  the roles that configure OAuth integrations — in practice, a platform admin. A normal ITIL user has none of them.
- **Only for the client credentials grant** (not the authorization code flow): the property
  **`glide.oauth.inbound.client.credential.grant_type.enabled`** must be created and set to `true`, and an **OAuth
  Application User** must be set on the record. Mentioned here only so nobody chases it for the wrong flow.

What missing prerequisites look like from outside, and why they mislead:

- **"System OAuth" is simply not in the navigator.** This reads as "my instance is broken" or "wrong release." It
  almost always means the plugin is inactive or the signed-in user lacks the role. The module is invisible rather than
  greyed out, so there is nothing to click that explains it.
- **The form saves but tokens never issue.** The plugin can be active while the activation property is false. The
  registry entry looks perfect, the client ID is there, and the token endpoint refuses — the failure surfaces at
  exchange time, hours later, on someone else's screen.
- **A 401 that has nothing to do with your credentials.** Instances sit behind IP access control and login policies;
  an instance that only accepts requests from allow-listed ranges will reject a correct client ID and secret in a way
  indistinguishable from a wrong one. Ask the customer's admin whether IP access control is on before debugging the
  credential.

## 3. The instance subdomain — mandatory, undiscoverable, and easy to collect in the wrong shape

Every URL in the flow is per customer:

```
https://{instance}.service-now.com/oauth_auth.do     # authorize
https://{instance}.service-now.com/oauth_token.do    # token exchange and refresh
https://{instance}.service-now.com/api               # REST API base (e.g. /api/now/table/incident)
```

There is no discovery endpoint, no email-domain lookup and no directory. **The customer tells you their instance
name, or the connection cannot be built.** So the connect experience must ask for it *before* the authorize redirect
— a connector that sends the user to a generic ServiceNow URL and hopes to learn the instance later has no URL to
send them to at all.

**Ask for the bare instance name** — `acme`, `acmedev`, `dev123456` — the label in front of `.service-now.com`. The
three answers customers actually give, and what to do about each:

| What they paste | What to do |
| --- | --- |
| `acme` | Correct. Use as-is |
| `acme.service-now.com` | Strip `.service-now.com` before use |
| `https://acme.service-now.com/now/nav/ui/…` | Take the first host label only |

Also worth knowing: large customers have several instances — production, one or more sub-production clones, and
developer instances — each with its own hostname, its own registry and its own data. "Our ServiceNow" is ambiguous.
Confirm which instance the connection is meant to reach, and expect the sub-production clone to contain a stale copy
of production data. Some customers front their instance with a vanity domain; if the host is not
`*.service-now.com`, stop and ask rather than guessing at the URL shape.

> **Product fact — as of 2026-09-20, this platform's ServiceNow connector** requires the instance value at connect
> time and substitutes it into all three URLs above. It performs **no normalisation of what the customer types**: the
> bare instance name works, and a value that already contains `.service-now.com` or a scheme produces a malformed
> host. Until that changes, a runbook and a connect-screen hint must both say **"just the instance name"** in so many
> words. Confirm with the connector's owner.

## 4. Create the registry entry

**All → System OAuth → Application Registry → New → "Create an OAuth API endpoint for external clients."**

Do not pick "Connect to a third party OAuth Provider" — that is the instance authenticating *outbound* to someone
else, and it produces no client ID for you. It is the single most common wrong turn on this form, because both
options live behind the same **New** button.

| Field | What matters |
| --- | --- |
| **Name** | Identifies the application requesting OAuth access; it is what the customer will see in their own registry later. Use your product's name so they can audit it |
| **Client ID** | **Auto-generated, read-only.** This is the `client_id`, and it is unique to this instance |
| **Client Secret** | The shared secret. Auto-generated if left blank. §7 |
| **Redirect URL** | The callback URL the authorization server redirects to. §5 |
| **Logo URL** | URL of an image to use as the application logo |
| **Active** | Must be ticked, or nothing authorizes |
| **Access Token Lifespan** | Seconds. Default **1,800** (30 minutes). §8 |
| **Refresh Token Lifespan** | Seconds. Default **8,640,000** (100 days). §8 |
| **Enforce Token Restrictions** | "Only allow tokens to be used with APIs set to allow the authentication profile." Leave as the customer's security team wants, but know that turning it on narrows what your token can call |
| **Client Type** | Iframe Embedded / Integration as a User / Integration as a Service. A customer-authorizes-us connector is the *user* shape; the service shape belongs with client credentials |
| **Token Format**, **Subject Claim** | Only relevant if the instance issues JWT-format tokens |
| **Comments** | Free text. Ask them to note who owns the integration — it is the field that stops the entry being deleted in a future audit |

On a newer instance the customer may instead be steered to the **Machine Identity Console**, which asks the same
questions through a per-grant-type wizard and stores the result in the same place. If their screen does not match
this table, that is the likely reason — have them say which release family the instance is on rather than improvising.

## 5. Redirect URL

The redirect URL is **yours**, not theirs, even though it is typed into their instance. It does not change per
customer; only the authorize and token hosts do (§3).

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

Because credentials are per instance, **each customer normally needs exactly one of these** — the one for the data
centre their workspace lives in. Confirm which before writing the runbook; handing a customer four URLs invites them
to pick the wrong one. Confirm the current list with the platform owner rather than assuming.

Notes that save a support round-trip:

- **Exact match.** Send the identical string on authorize and again on the token exchange. No trailing slash, no
  added query string, no subpath forgiveness.
- The documented example for this field is of the form `…/oauth_redirect.do`, which is the path ServiceNow's *own*
  callback uses when an instance is the OAuth **client**. It is an example, not a requirement, and it confuses
  admins into typing their own instance's `oauth_redirect.do` here. Your URL is the one that belongs in this field.
- The field is documented in the singular. Comma-separating several URLs is widely reported to work and is not
  documented; if a customer genuinely needs more than one, have them verify it on their instance instead of assuming,
  or create a second registry entry.
- Changing the redirect URL later is a form edit on their side and takes effect immediately — unlike most portals,
  nothing is frozen. That is the one respect in which ServiceNow is more forgiving than a central provider.

## 6. "Scopes" — ServiceNow does not have them in the usual sense

There is no list of scope strings to request, no consent screen enumerating permissions, and nothing to line up
between the registry entry and the authorize URL. The documented default and only scope for ServiceNow as an OAuth
provider is **`useraccount`**, and it is an override rather than a restriction: an entity associated with it "can
access any API even if you have created a REST API Auth Scope record with a different auth scope."

**Access is the authorizing user's roles and ACLs.** The token acts as that person. The Table API documentation is
blunt about it: "The calling user must have sufficient roles to access the data in the table specified in the
request." ACLs are then evaluated per record and per field.

The consequence is concrete and is the single most common "the integration is broken" ticket:

> A read of a table the user can see, but whose rows or fields they cannot, returns **HTTP 200 with fewer rows than
> the table holds — or rows with fields blanked out — and no error at all.** Version 2 of the Table API returns 200
> and an empty array when nothing matches or nothing is visible. Nothing in the response says "filtered." A customer
> looking at 4,000 incidents in their browser and 120 in your product is looking at an ACL result, not a pagination
> bug.

So **least privilege here means the identity, not the request**:

1. Have the customer create a **dedicated integration user** rather than authorizing as a named human. A human's
   token dies with their offboarding, and carries their entire access.
2. Give that user the **narrowest role that covers the tables the connector reads**, and no more. Ask their admin to
   confirm read access to each table the connector touches, by name, before go-live — it is much cheaper than
   discovering it as missing rows three weeks later.
3. Expect their admin to want **REST API Auth Scope** on top: scope records link an auth scope to a specific REST API,
   method, version and resource, and once one is enabled on an API, "all the existing OAuth token won't able to access
   this API anymore unless admin adds this auth scope to the corresponded OAuth entity." That is an instance-side
   change that can break a working connection with no action on your side — so if a customer's connection starts
   returning 403s for one endpoint only, this is the first thing to ask about.
4. Remember **domain separation** on large instances: a user scoped to one domain sees only that domain's records,
   which is another silent row filter.

> **Product fact — as of 2026-09-20, this platform's ServiceNow connector** sends **no `scope` parameter** on the
> authorize call, which matches ServiceNow's model. Its `scope` configuration is present but commented out, with
> `useraccount` as the value it would send. It reads and writes through the Table API against the incident, user,
> journal-field and knowledge-base tables, and lists an attachment-backed file object — so the integration user's role
> must cover exactly those tables, read and (where the workspace writes) write. Confirm the current object set with
> the connector's owner before telling a customer's admin which role to grant.

## 7. Capture the credentials

Per customer, collect and store:

- **Client ID** and **client secret**, from their registry entry
- **Instance subdomain**, in the bare form (§3)
- Which **user** authorized, and with what role
- The **Access Token Lifespan** and **Refresh Token Lifespan** values on that record (§8)
- Whether **Enforce Token Restrictions** is on, and whether any REST API Auth Scope applies (§6)

**Treat the secret as write-once.** It is stored in an encrypted field type on the record; depending on the instance
and release, an admin may not be able to read it back afterwards, only replace it. Tell the customer to copy it at
creation. If it is lost, the recovery is to set a new one — which is a rotation (below), not a retrieval.

**Rotation breaks every exchange and refresh for that one customer** until the new value is deployed. Access tokens
already issued keep working until they expire, which by default is 30 minutes. Never rotate without explicit
go-ahead. The upside of the per-instance model is that the blast radius is one customer, not all of them.

**Getting the secret from the customer to you is part of the runbook.** Ask for it through whatever secret-sharing
channel the platform already uses. Do not accept it in a ticket comment, an email thread or a chat message, and do
not paste it into one yourself. If a customer sends it in the clear anyway, say so and have them rotate it once the
connection is up.

> **Product fact — as of 2026-09-20, this platform's ServiceNow connector** exchanges the code with a form-encoded
> POST carrying `client_id`, `client_secret`, `grant_type`, `code` and `redirect_uri` — credentials in the body, not
> HTTP Basic — and refreshes with `client_id`, `client_secret`, `grant_type` and `refresh_token`. It also sends PKCE
> (`code_challenge_method=S256` on authorize, a verifier on exchange) alongside the client secret. ServiceNow
> documents PKCE for public clients on the authorization code grant; the confidential-client-plus-PKCE combination is
> worth confirming on a test instance rather than assuming. Confirm all of this with the connector's owner.

## 8. Tokens: nothing here is a constant

- **Access token**: default **1,800 seconds (30 minutes)**.
- **Refresh token**: default **8,640,000 seconds (100 days)**.
- **Both are fields on the customer's registry record.** A security-conscious admin may set the access token to five
  minutes; a lax one may extend it. **The connector may not hard-code either.** Store `expires_in` from the response
  and refresh on it, and treat a 401 as "refresh once and retry," not "the customer must re-authorize."
- **The 100-day refresh token is the quiet one.** A customer who connects once and is never re-authorized will drop
  off roughly three months later with no warning, mid-quarter, looking like an outage. Whatever the platform does
  about expiring refresh tokens — proactive refresh, re-consent email, an alert — decide it deliberately rather than
  discovering it.
- **Revocation is theirs.** Deactivating the registry entry, deactivating or locking the integration user, changing
  their roles, or deleting the token record all stop your access immediately and without notice to you.
- **Rate limits are per instance.** Rules live in `sys_rate_limit_rules` as a *request limit per hour*, applied to a
  single user, a role, or all users, and created by a `rate_limit_admin`. No default limit is documented, so the
  honest statement is that you cannot know a customer's budget without asking. Over the limit is **429** with
  **`Retry-After`** in seconds — honour it. An hourly budget behaves differently from a per-minute one: a backfill
  can consume the whole hour's allowance in two minutes and then stall for fifty-eight.

> **Product fact — as of 2026-09-20, this platform's ServiceNow connector** implements the refresh grant. Its list
> calls page with an offset parameter and a limit parameter, up to 10,000 records per page, and suppress the row
> count. That page size matches the Table API's documented default maximum, and ServiceNow warns that large values
> affect performance — on a slow instance a full-page read is the thing that trips a customer's rate limit rule.
> Confirm with the connector's owner.

## 9. A test instance: the Personal Developer Instance, and its two surprises

You cannot rehearse this against a customer. Get a **Personal Developer Instance (PDI)** from ServiceNow's developer
site: free, full platform, its own `*.service-now.com` hostname, and admin rights — which is what you need, since
creating a registry entry requires `oauth_admin`.

Two behaviours catch teams mid-project:

1. **It hibernates.** An idle PDI shuts down its database and application server. Waking it from the Developer Site
   "usually take[s] three to five minutes … and should not take any longer than 20 minutes." A pipeline or a
   regression suite pointed at a hibernating PDI does not get a clean error; it gets timeouts and a very confused
   afternoon.
2. **It can be reclaimed.** ServiceNow emails when a PDI has been inactive for over 10 days, and a reclaimed PDI "is
   reset to its original state and reassigned to another Developer Program member." Everything on it — your registry
   entry, its client ID and secret, your test data — is gone, and the same credentials will never come back. Signing
   in to the Developer Site at least weekly is the documented way to keep it.

So: never make a PDI the only home of anything you cannot recreate, keep the registry entry's creation steps in the
runbook rather than in your head, and if a test connection breaks after a quiet fortnight, check whether the instance
still belongs to you before debugging the connector.

A customer's own **sub-production (development or test) instance** is the other useful target, and is the right place
to have them rehearse the runbook before touching production. Note its registry is separate from production's, so the
credentials do not carry over.

## 10. Verify end-to-end

Testing as an instance admin proves almost nothing — an admin sees everything, so every ACL problem in §6 stays
invisible. Test the path a customer's integration user actually takes:

1. Authorize through the platform's real connect flow, supplying the instance name the way a customer would.
2. Authorize as the **dedicated integration user with its narrow role**, not as `admin`.
3. Confirm the token response carries a **`refresh_token`** and an **`expires_in`**, and store both.
4. **Force a refresh** and make a read call with the new token. This is the check that catches a connector built for
   tokens that do not expire.
5. Read **one object per supported type**, and compare the row count against what that same user sees in the
   ServiceNow UI. Equal counts mean the role is right; fewer rows through the API means ACLs are filtering (§6).
6. Repeat the whole thing against a **second, unrelated instance** — a colleague's PDI is enough. The per-instance
   model means the second instance is the first honest test of the runbook.

| Symptom | Cause |
| --- | --- |
| "System OAuth" missing from the navigator | OAuth plugin inactive, or the signed-in user lacks `oauth_admin` (§2) |
| Registry entry looks correct, token endpoint refuses | `com.snc.platform.security.oauth.is.active` is not `true` (§2) |
| Authorize URL 404s or shows the instance's login page forever | Wrong instance host, or a value entered with `.service-now.com` or a scheme already in it (§3) |
| No client ID and secret anywhere on the form | "Connect to a third party OAuth Provider" was chosen instead of the external-clients option (§4) |
| Redirect rejected, or exchange fails after a good authorize | Redirect URL not identical between the registry entry, the authorize call and the exchange (§5) |
| One customer's credentials work; another's are rejected | Expected — client IDs are per instance and never portable (§1) |
| 200 OK, but far fewer rows than the customer sees | The authorizing user's roles and ACLs, or domain separation (§6) |
| Fields present in the UI arrive empty over the API | Field-level ACLs on those fields (§6) |
| 403 on one endpoint only, everything else fine | A REST API Auth Scope record now covers that API and the OAuth entity is not linked to it (§6) |
| Works for 30 minutes, then 401 on everything | Access token expired and the client did not refresh (§8) |
| Connections die roughly three months after connecting | The 100-day refresh token lifespan elapsed (§8) |
| Sudden 401s across every call for one customer | The registry entry was deactivated, or the integration user was locked or had roles removed (§8) |
| 429 with `Retry-After` during a backfill | The instance's hourly rate limit rule; honour the header and pace the job (§8) |
| Correct credentials rejected from your servers only | IP access control on the instance (§2) |
| Test instance stops responding after a quiet week | PDI hibernating — or reclaimed (§9) |

## 11. Hand off — never commit the secret

- **Do not** write a client secret, a ServiceNow password or an access token into source control, a test, a fixture, a
  committed `.env`, a ticket, a PR body or a chat channel. Values go to the user, for their secret store.
- If a code change is needed (instance-name normalisation, refresh handling, pagination), keep it secret-free and say
  what the human must set out of band.
- The customer-facing artefact is a **runbook**, and it should contain: the click path (§4), the literal redirect URL
  for their data centre (§5), the instance-name format you need (§3), the integration-user and role guidance (§6), the
  two lifespan fields to leave alone or to tell you about (§8), and how to return the client ID and secret safely (§7).
- Close with: whose instance; the instance subdomain; the client ID; where the secret was delivered; which user
  authorizes and with what role; the two lifespan values on the record; whether Enforce Token Restrictions or any REST
  API Auth Scope applies; what was verified end-to-end and on which instance; and anything left for the customer.

## Stop and ask

Hand back to a human rather than guessing when: someone expects a single shared ServiceNow OAuth client and the
per-instance reality changes the product plan (§1); nobody with `oauth_admin` on the target instance has been
identified; a customer proposes handing over a human's username and password, or their own admin credentials (§1);
the role to grant the integration user is a security decision their admin has not made (§6); an instance-side REST API
Auth Scope or Enforce Token Restrictions setting would have to be changed (§6); the customer has multiple instances
and nobody has said which one is in scope (§3); the instance is fronted by a vanity domain or behind IP access control
(§2, §3); a client secret needs rotating on a live connection (§7); or the instance's UI does not match the
**Platform state** section above.

## References

- OAuth 2.0 overview — https://www.servicenow.com/docs/r/australia/platform-security/authentication/c_OAuthApplications.html
- Set up OAuth (plugin and activation property) — https://www.servicenow.com/docs/bundle/australia-platform-security/page/administer/security/task/t_SettingUpOAuth.html
- Create an OAuth API endpoint for external clients (the field table) — https://www.servicenow.com/docs/bundle/australia-platform-security/page/administer/security/task/t_CreateEndpointforExternalClients.html
- OAuth authorization code grant flow — https://www.servicenow.com/docs/bundle/australia-platform-security/page/administer/security/concept/c_OAuthAuthorizationCodeFlow.html
- Authorize access to the endpoint (the user-facing consent step) — https://www.servicenow.com/docs/bundle/australia-platform-security/page/administer/security/task/t_AuthorizeAccessEndpiont.html
- Authorization code grant (confidential vs public clients, PKCE) — https://www.servicenow.com/docs/r/australia/platform-security/authentication/authorization-code-grant.html
- OAuth inbound (grant types, roles) — https://www.servicenow.com/docs/r/australia/platform-security/authentication/oauth-inbound.html
- OAuth inbound and outbound — https://www.servicenow.com/docs/r/australia/platform-security/authentication/oauth-inbound-and-outbound.html
- REST API Auth Scope (how instance-side scoping really works) — https://www.servicenow.com/docs/r/australia/platform-security/authentication/rest-api-auth-scope.html
- Configure auth scope — https://www.servicenow.com/docs/r/australia/platform-security/authentication/configure-rest-api-auth-scope.html
- Add the OAuth Application User (client credentials only) — https://www.servicenow.com/docs/r/australia/platform-security/authentication/add-oauth-application-user.html
- Create the client credentials system property — https://www.servicenow.com/docs/r/australia/platform-security/authentication/create-cc-sys-prop.html
- Machine Identity Console — https://www.servicenow.com/docs/r/australia/platform-security/identity/machine-identity-console.html
- Inbound integrations in the Machine Identity Console — https://www.servicenow.com/docs/r/australia/platform-security/identity/inbound-integrations.html
- Table API (roles, ACLs, sysparm parameters, 200-with-empty-array) — https://www.servicenow.com/docs/bundle/australia-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html
- Inbound REST API rate limiting (429 and Retry-After) — https://www.servicenow.com/docs/bundle/australia-api-reference/page/integrate/inbound-rest/concept/inbound-REST-API-rate-limiting.html
- Create an inbound REST API rate limit rule — https://www.servicenow.com/docs/bundle/australia-api-reference/page/integrate/inbound-rest/task/create-REST-API-rate-limits.html
- REST API overview — https://www.servicenow.com/docs/bundle/australia-api-reference/page/build/applications/concept/api-rest.html
- Australia release notes (current release family) — https://www.servicenow.com/docs/r/australia/release-notes/family-release-notes.html
- Personal Developer Instance FAQ (hibernation, 10-day reclamation) — https://developer.servicenow.com/print_page.do?release=yokohama&category=now-platform&identifier=pdi_faq&module=guide
- ServiceNow Developer Program (get a PDI) — https://developer.servicenow.com/dev.do
