> For the complete documentation index, see [llms.txt](https://skyforgeai.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://skyforgeai.gitbook.io/docs/connect/servicenow-connection.md).

# Connecting to ServiceNow

SkyForge supports three ways to authenticate against a ServiceNow instance. All are configured from the Hub — no manual config file editing required.

| Method                                             | Use when                                                |
| -------------------------------------------------- | ------------------------------------------------------- |
| [**OAuth 2.0**](#oauth-20)                         | Production, and anywhere you can create an OAuth client |
| [**Basic Auth**](#basic-auth)                      | Developer instances only                                |
| [**Browser Session / SSO**](#browser-session--sso) | SAML/Okta/Azure AD instances with no local password     |

***

## Adding an instance

SkyForge opens on the Hub. Go to the **Instance** tab in the left nav → **+ Add Instance**, then pick your method.

![Hub Instance tab — + Add Instance is in the top right](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-6a0f39d230d91f1c68ec3aba7ca7a506e7809e0a%2Fhub-instance-tab.png?alt=media)

![Select an authentication method](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-091ebd4384e16a2f01f61192e65e0b6dc83a2f8b%2Fadd-instance-method.png?alt=media)

Each instance row has **Test**, **Set Active**, **Edit**, and **Remove**. Use **Edit** to re-enter credentials after changing anything on the ServiceNow side.

Instances added here are **deploy** instances — the ones SkyForge builds against. A story source is added separately from the **Stories** tab and kept apart on purpose, so reading stories from one instance while building against another is the normal case rather than a workaround.

***

## OAuth 2.0

*Recommended for production.*

OAuth is the only method that works on production instances, never exposes a password, and refreshes itself. It takes two steps: create an OAuth client in ServiceNow, then connect SkyForge to it.

### Step 1 — Create the OAuth client in ServiceNow

ServiceNow replaced the OAuth setup screens in the **Zurich** release. Both forms create the same kind of client and both work with SkyForge — only the layout and field names differ.

Go to **System OAuth → Application Registry** and click **New**. What you see next tells you which path to follow:

| What you see                                             | Release         | Follow                                               |
| -------------------------------------------------------- | --------------- | ---------------------------------------------------- |
| **New Inbound Integration Experience** listed at the top | Zurich or newer | [Path A](#path-a-new-inbound-integration-experience) |
| Only `Create an OAuth API endpoint for external clients` | Pre-Zurich      | [Path B](#path-b-application-registry-pre-zurich)    |

> **The `[Deprecated UI]` label is about the form, not the protocol.** On Zurich+ instances the old menu entries are prefixed with `[Deprecated UI]`. They still work, and the underlying OAuth endpoints (`/oauth_auth.do`, `/oauth_token.do`) are unchanged. If you have working OAuth clients from before, you don't need to recreate them.

### Path A: New Inbound Integration Experience

*Zurich and newer.*

#### Step A1 — Pick the grant type

**System OAuth → Application Registry → New → New Inbound Integration Experience → New integration.**

![The five inbound integration connection types](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-67f45db42ed13dfbe295e67e69b4f5001e514153%2Foauth-nie-grant-types.png?alt=media)

Choose **OAuth - Authorization code grant**.

> Don't pick **Third party ID token issued by OIDC supporting identity provider** — that's for federated setups where an external IdP issues the token. SkyForge needs ServiceNow itself to issue it.
>
> **Client credentials grant** won't work either. SkyForge acts on behalf of a signed-in user, and client credentials has no user context.

#### Step A2 — Fill in the details

![The Details section filled in for SkyForge](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-96d9b3ffd0124090e5b2d4d737d62e0475c6e529%2Foauth-nie-details-filled.png?alt=media)

| Field                       | Value                            | Notes                                                                                              |
| --------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Name**                    | `SkyForge`                       | Cosmetic                                                                                           |
| **Provider name**           | `SkyForge`                       | Required. The dropdown suggests Auth0/Google/Microsoft/Okta — ignore those and type your own value |
| **Redirect URLs**           | `http://localhost:3005/callback` | **Critical** — must match exactly, no trailing slash                                               |
| **This is a public client** | ☐ unchecked                      | **Critical** — checking it skips client secret validation and SkyForge's secret will be rejected   |
| **Active**                  | ☑ checked                        | Inactive clients reject requests silently                                                          |
| **Comments**                | anything                         | Optional                                                                                           |

**Client ID** and **Client secret** are generated for you. Don't copy them yet — they regenerate on save.

#### Step A3 — Turn off scope restriction

Scroll to **Auth scope**.

![Auth scope and Advanced options configured correctly](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-22048c411f7e8f49d5441f1875ca393fba3ab35c%2Foauth-nie-scope-advanced.png?alt=media)

Two separate checkboxes both restrict API access, and **both must be off**:

| Setting                                         | Value       | Why                                                                                         |
| ----------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- |
| **Allow access only to APIs in selected scope** | ☐ unchecked | **Critical** — defaults to *checked*. Leave it on and SkyForge gets 403s on most tool calls |
| **Enforce token restriction**                   | ☐ unchecked | **Critical** — limits the client to APIs named in REST API Access Policies                  |
| **Auth scope** row                              | leave empty | Adding a row re-enables allow-list behaviour                                                |

With both off, access is governed by the OAuth user's roles and ACLs instead of an explicit API allow-list — which is what SkyForge needs, since it reaches a wide range of platform tables.

> **"Recommended to enable" badge:** ServiceNow shows this next to the scope checkbox as generic security guidance. For SkyForge, leave it unchecked and control access through the roles on the OAuth user instead. See [Locking it down](#locking-it-down) below.

#### Step A4 — Check the token lifespans

In the same **Advanced options** section:

| Field                      | Value     | Notes                                                                                        |
| -------------------------- | --------- | -------------------------------------------------------------------------------------------- |
| **Access token lifespan**  | `1800`    | Platform default. SkyForge refreshes on expiry                                               |
| **Refresh token lifespan** | `8640000` | Platform default (100 days). Never set this to `0` — refresh tokens would be dead on arrival |
| **Token Format**           | `Opaque`  | Default. `JWT` also works                                                                    |
| **Login URL / Logo URL**   | blank     | Cosmetic                                                                                     |

There is **no PKCE toggle** on this form — PKCE is always on for the authorization code grant. SkyForge sends `code_challenge` (S256) and `code_verifier` on every flow, so this needs no configuration.

#### Step A5 — Save

Click **Save**. A confirmation dialog appears:

![The Assign auth scope confirmation dialog](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-ddec67d48b9d71f124c579d946726a7d6b81f621%2Foauth-nie-skip-auth-scope.png?alt=media)

Click **Skip for now** — this is expected, since Step A3 intentionally left the auth scope empty.

Now reopen the saved record and copy the **Client ID** and unmask the **Client secret**.

Continue to [Step 2 — Connect SkyForge](#step-2--connect-skyforge).

> **GitHub Codespaces:** SkyForge detects `CODESPACE_NAME` and `GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN` and uses the forwarded URL `https://<codespace-name>-3005.<forwarding-domain>/callback` instead of localhost. Add that as a second Redirect URL on the OAuth client — a Codespace cannot receive the localhost callback.

***

### Path B: Application Registry (pre-Zurich)

*Washington DC, Xanadu, Yokohama.*

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

| Field                      | Value                            | Notes                                        |
| -------------------------- | -------------------------------- | -------------------------------------------- |
| **Name**                   | `SkyForge`                       | Cosmetic                                     |
| **Redirect URL**           | `http://localhost:3005/callback` | **Critical** — must match exactly            |
| **Active**                 | ☑ checked                        | Inactive apps reject requests silently       |
| **Accessible from**        | `All application scopes`         | Required for global scope                    |
| **Scope Restriction**      | `Broadly scoped`                 | **Critical** — `Securely scoped` returns 403 |
| **Access Token Lifespan**  | `1800` or `18000`                | Either works                                 |
| **Refresh Token Lifespan** | `8640000`                        | **Critical** — `0` breaks refresh            |

Submit, then open the record and copy the **Client ID** and **Client Secret**.

> `Scope Restriction = Broadly scoped` here is the same setting as *"Allow access only to APIs in selected scope" unchecked* in Path A. Same behaviour, different wording.

***

### Step 2 — Connect SkyForge

Same for both paths.

1. In the Hub, go to the **Instance** tab in the left nav → **+ Add Instance**
2. Choose **OAuth 2.0**
3. Enter your instance URL, Client ID, and Client Secret
4. Click **Connect** — your browser opens the ServiceNow login
5. Click **Allow**

Tokens are stored locally and refreshed automatically — you shouldn't need to re-authenticate unless you change the OAuth client in ServiceNow.

#### Verify it worked

In the **Hub → Instance** tab, click **Test** on your instance — it confirms the credentials reach ServiceNow.

For an end-to-end check, start a session and run:

```
Give me the last 5 updated incidents
```

If that returns records, your OAuth client is configured correctly.

***

### Locking it down

Both paths above turn off API scope restriction, so access is governed entirely by the **OAuth user's roles and ACLs**. On production, that makes the user account the security boundary:

* Use a dedicated service account, not a personal admin login
* Grant only the roles SkyForge actually needs for your use case
* Don't use an account with `admin` unless you intend SkyForge to have admin reach

***

## Basic Auth

*Developer instances only.*

{% hint style="danger" %}
**Prerequisite — Australia release and later:** the account must have the `snc_basic_auth_api_access` role before you connect. Without it, every REST API call returns **401 Unauthorized** even when the username and password are correct.

1. Go to **User Administration → Users** → open the account
2. Scroll to the **Roles** related list → **Edit**
3. Add `snc_basic_auth_api_access` → **Save**

This applies to all instances on the Australia release or newer. On older releases the role doesn't exist and isn't needed.
{% endhint %}

Hub → Instance → + Add Instance → **Basic Auth** → enter instance URL, username, password → **Connect**.

![Basic Auth form filled in](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-2a71560607cd491ae5ec0586215c6187e576e9fa%2Fadd-instance-basic-filled.png?alt=media)

SkyForge derives the instance name from the URL as you type it — `dev232324` above — and that name is what identifies the instance everywhere else in the Hub.

> ⚠️ Never use Basic Auth in production.

***

## Browser Session / SSO

Use this when your instance requires SSO (Okta, Azure AD, SAML) and you have no local password.

{% hint style="danger" %}
**Prerequisite — Australia release and later:** the account must have the `snc_basic_auth_api_access` role. Signing in through SSO gets you a working browser session, but REST API calls made with that session still return **401** without this role — so SkyForge connects and then fails on every tool call.

1. Go to **User Administration → Users** → open the account
2. Scroll to the **Roles** related list → **Edit**
3. Add `snc_basic_auth_api_access` → **Save**

If calls still return 401 or 403 after adding it, the instance may also have REST API ACLs active, which additionally require `snc_platform_rest_api_access`.
{% endhint %}

1. Open your ServiceNow instance in Chrome/Firefox
2. Open **DevTools → Network tab** (F12)
3. Filter by `api/now` → right-click any `/api/now/table/` request → **Copy → Copy as cURL**

> The exact label varies by browser: Firefox and older Chrome show **Copy as cURL**; current Chrome and Edge show **Copy as cURL (bash)** (with a separate **Copy as cURL (cmd)** for a Windows-style command). Pick the `bash` variant if you're offered a choice — SkyForge expects a POSIX-style cURL command, not the `cmd` one.

![Chrome DevTools — right-click a request → Copy → Copy as cURL](https://2874025704-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoJEVszlHbBuPIN3VRjBS%2Fuploads%2Fgit-blob-093dc2db15d6feb7f14983989dfb476f7209d68f%2Fdevtools-copy-as-curl.png?alt=media)

4. Hub → Instance → + Add Instance → Browser Session → paste cURL → **Connect**

> Session lasts \~8 hours. Re-paste a fresh cURL when expired.

> SkyForge replays the User-Agent captured in your cURL alongside the cookies — some corporate proxies/WAFs (Zscaler, Akamai, F5, etc.) reject requests where the User-Agent doesn't match what established the session, so this matters even if the cookies themselves are valid. If your instance still refuses the pasted session, that's usually IP-binding on the instance/network side (the session tied to the network it was captured on) rather than anything SkyForge can work around — running SkyForge from that same network, or switching to OAuth 2.0 (works from anywhere, no network dependency), are the two ways around it.

***

## Multiple Instances

```jsonc
{
  "mcp": {
    "servicenow-dev": {
      "type": "local",
      "command": ["skyforge", "mcp", "start"],
      "environment": {
        "SERVICENOW_INSTANCE_URL": "https://dev12345.service-now.com",
        "SERVICENOW_CLIENT_ID": "dev-client-id",
        "SERVICENOW_CLIENT_SECRET": "dev-secret"
      }
    },
    "servicenow-prod": {
      "type": "local",
      "command": ["skyforge", "mcp", "start"],
      "environment": {
        "SERVICENOW_INSTANCE_URL": "https://prod.service-now.com",
        "SERVICENOW_CLIENT_ID": "prod-client-id",
        "SERVICENOW_CLIENT_SECRET": "prod-secret"
      }
    }
  }
}
```

***

## Need help?

[Submit an issue →](https://github.com/tryskyforge/skyforgeissues)
