---
name: rippling-oauth-app
description: Establishes how a Rippling OAuth 2.0 client ID and secret is actually obtained — not from a self-serve developer console but from an App Shop partner application that Rippling approves by hand, after which credentials appear inside a partner company's app listing (sandbox pair first, production pair only after beta approval) — and covers the two API generations (legacy V1 vs the current REST API), the scope catalog and its public/private split, the install-not-consent flow, redirect URLs, token and refresh lifetimes, rate limits, test companies, and a symptom-to-cause table. Use when asked to get Rippling OAuth credentials, register a Rippling app, become a Rippling App Shop partner, obtain a Rippling sandbox or test company, help a customer create a Rippling API token, or fix a Rippling auth error like `Invalid Client` on install, a 403 carrying "The token has been revoked", or fields coming back null. For any other vendor's developer portal, use that vendor's skill instead.
---

# Rippling OAuth Credentials

**Read this first: there is no Rippling developer console where you sign up, create an app and walk away with a
client ID and secret.** Credentials live inside an *app listing*, an app listing lives inside a *partner company*, and
a partner company only exists because Rippling created one for you after approving an application. Rippling's own
integration checklist starts at "Apply for listing in the App Shop" and continues "If approved, look out for your
onboarding email" — the email carries login credentials for a partner company and a test company that Rippling
provisions. Everything technical comes after that approval. There is no paid tier, no instant path, and nothing to
click your way into.

Worse for a platform that connects *other organizations'* Rippling accounts on behalf of many customers: Rippling's
Partner Requirements page addresses that model directly and rules it out. **REQ2** reads "Rippling does not support
partners integrating through third-party integration services. All integrations must be built directly to Rippling.
3rd-party vendors using API keys are not supported… Partner integrations must leverage OAuth." The same page's
standing note says "The use of non-sanctioned developer tools (apart from approved Rippling partners
[merge.dev and tryfinch.com]) is prohibited," and points at Sections 2.4 and 3.1.4 of the Rippling Developer Terms of
Use. Two named aggregators are sanctioned; the rest are not. **If the run is "get us Rippling OAuth credentials so our
integration platform can connect customers' Rippling accounts," the honest first reply is that this is the exact
arrangement REQ2 names, and the question is a business one — for Rippling's partnerships team, not for a form.**
Stop at §2 and hand it back.

Three more things cost real time here. **There are two live API generations** and the docs for both are reachable;
picking the retired one is the expensive mistake (§7). **Scopes are fixed on the app listing, not chosen per customer
at authorize time** (§6), which breaks the usual per-object-scope design. And **the customer's own admin installs your
app from inside Rippling**, so your connect flow is one leg of an install that must return to Rippling to finish
(§8).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Partner status** | Has the App Shop application been submitted? Approved? Does a partner company exist? (§2) |
| **New listing, or an edit to an existing one** | A new listing means a new client ID and every existing install re-authorizes (§1) |
| **Which API generation** | New work is REST; only pre-existing legacy partners stay on V1 (§7) |
| **App listing name** | Appears in the App Shop and inside the install and authorize URLs (§4) |
| **Category, icon, descriptions** | Required before an install will work at all (§4) |
| **Default redirect URL, plus any additional ones** | Exact strings; the default is the only one used for Rippling-initiated installs (§5) |
| **Scope set** | Exact catalog strings, and whether any are private scopes needing justification (§6) |
| **Environment** | Sandbox and production are two credential pairs on the same listing (§9) |
| **Who owns the partner company login** | A human receives the onboarding email and does two-factor setup (§3) |
| **Whether SSO and user management are in scope** | Rippling requires both by default of listed apps (§11) |

## Quick Start

1. Establish whether a partnership exists at all — without it there are no credentials, at any price (§2).
2. Confirm whether an existing listing should be reused rather than replaced (§1).
3. Apply through Rippling's App Shop application; wait for the onboarding email with partner and test company logins (§2, §3).
4. Create an app listing in the partner company and configure name, category, icon, redirect URL and scopes (§4, §5, §6).
5. Target the **REST API** unless you are a pre-November-2025 legacy partner (§7).
6. Deploy the **sandbox** app, which is what makes the sandbox client ID and secret appear (§9).
7. Install the sandbox app in the **test company** and complete a real install round trip (§8, §12).
8. Submit the Integration Review form for beta; production credentials appear only after that approval (§9, §11).
9. Get three outside companies to install, submit Launch Review, publish (§11).
10. Verify end-to-end, then hand the secret to a human — never to source control (§12, §13).

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

Everything below came from fetching Rippling's own developer documentation and probing its hosts directly, with no
browser session and no partner login. If the portal or docs do not look like this, stop and report what you actually
see rather than clicking on.

- **Credentials are partner-gated, not self-serve.** The documented sequence is: apply → approval → onboarding email →
  partner company → app listing → deploy sandbox → sandbox credentials → Integration Review → beta approval →
  production credentials. The sandbox client ID and secret "will only display… if you have already deployed your
  sandbox app listing"; the production pair appears only "once the app listing has been approved for beta testing."
- **There are two API generations, both documented, both reachable.** Rippling's API Access page states: "As of
  November 2025, **all new App Shop partners** will use Rippling's **V2 REST API endpoints**. **Legacy partners** will
  continue to use Rippling's **V1 API endpoints**." New work targets REST. The V1 material is still published under
  its own legacy section for the partners already on it.
- **REST API host:** `https://rest.ripplingapis.com`, with `https://rest.ripplingpreviewapis.com` published as a
  Preview endpoint. Both answered `401` to an unauthenticated read on 2026-09-20, which is how an authenticated host
  should answer.
- **OAuth endpoints are on the `app.rippling.com` / `api.rippling.com` side, not on the REST host.** The install guide
  and the OIDC guide both name **`https://api.rippling.com/api/o/token/`** as the token endpoint for the
  authorization-code exchange and for refresh.
- **Rippling's own machine-readable REST spec disagrees with its guides about the OAuth endpoints, and the spec is the
  one that is wrong.** The published spec (`https://developer.rippling.com/docs/rest-beta.yaml`, version `2024-08-01`
  on 2026-09-20) carries `authorizationUrl: https://app.rippling.com/o/authorize` and
  `tokenUrl: https://app.rippling.com/o/token`. A `POST` to `https://app.rippling.com/o/token` returned **404** on
  2026-09-20, while `https://api.rippling.com/api/o/token/` returned **400** (i.e. it exists and rejected the empty
  request). Do not generate a client from that security scheme and assume the URLs.
- **The OIDC authorize endpoint is `https://app.rippling.com/oidc/v1/authorize`, and Rippling says not to use it for
  installs.** Verbatim: "The OIDC flow should only be used after your app is installed in Rippling… The OIDC authorize
  endpoint should not be used as part the initial app installation flow or integration set up."
- **The scope catalog is published and is REST-shaped**: `<resource>.read` / `<resource>.read-write`, e.g.
  `workers.read`, `work-locations.read-write`, `leave-requests.read`. The published spec carried roughly 250 scope
  entries on 2026-09-20. V1's grammar was different (`employee:name`, `company:teams`), and the two do not mix.
- **Rate limits are a burst threshold, not a quota**: 300 requests per IP per sliding 10-second window, with a
  10-second penalty during which everything is rejected. Pagination defaults to 50 and caps at 100; filters cap at 64
  nodes; there is also a maximum expand depth. "There are no endpoint-specific limits as of today."
- **Rippling explicitly disallows third-party integration services other than two named aggregators** (§intro, REQ2).
  This is the single most important fact on the page for a multi-tenant integration platform.
- **Both `developer.rippling.com` and `app.rippling.com` are single-page apps that return HTTP 200 for any path,
  including nonsense ones.** A 200 from either proves nothing about a URL existing. The doc URLs in §References were
  confirmed against the site's own route manifest, not by status code alone.
- **The Developer Terms of Use at `https://app.rippling.com/developer/tos` resolve but render client-side**, so the
  text of Sections 2.4 and 3.1.4 could not be read without a browser. Treat their content as unverified here and read
  them before relying on any summary of them.
- **The REST API is actively developed.** Changelog entries dated 2026-08-25, 2026-08-28, 2026-09-01 and 2026-09-16
  add endpoints, scopes and worker fields. This is the generation receiving investment.

## 1. Decide: reuse the existing listing, or create a new one

**A new app listing means a new client ID, and every existing customer install is bound to the old one.** Every
customer would have to install again. Reuse the existing listing for: adding a redirect URL, adding a scope, changing
listing copy, or diagnosing a failure — Rippling supports all of these in place through *listing versions*, which let
you stage and test changes without touching live customers.

Create a new listing only when the user explicitly wants one: a genuinely separate product, or a replacement for a
compromised client. Note two constraints that shape this decision:

- **Only one listing version can be deployed as the sandbox at a time.** To test a new configuration you duplicate the
  listing version and replace the deployed sandbox app.
- **Adding scopes does not reach existing installs.** Rippling: "current installs will need to reauthorize the app in
  order to grant the app access to the new scopes." Every already-connected customer must be walked back through the
  authorize step. Plan scope changes as customer-facing migrations, not config edits.

Say which path you are taking before you touch anything.

## 2. The gate: applying to the App Shop

**This is where most runs end, and ending here is a correct outcome.**

Rippling's integration checklist begins: "To develop an integration for Rippling's App Shop, you will need to follow
the development process listed below… Apply for listing in the App Shop" — through Rippling's public service desk at
`https://rippling-public.atlassian.net/servicedesk/customer/portal/3`. Then: "If approved, look out for your
onboarding email."

What to know before starting:

- **Approval precedes everything technical.** No partner company, no test company, no listing, no credentials.
- **There is no fee.** "The usage of the Rippling API is completely free to partners as of now" and "there is no cost
  to the app partner to list their app on the Rippling App Shop." The cost lands on the *customer*: per REQ1, every
  partner integration requires the customer to hold Rippling's **Identity & Access Management** package. A customer
  without it cannot install your app no matter how correct your OAuth is.
- **Rippling sets requirements that are not optional for a listed app** (§11): SSO by default, user management by
  default, pagination, refresh-token handling, and a Rippling-initiated install flow that ends back in Rippling.
- **Do not promise dates.** REQ5: partners agree "not to commit to timelines or promise launch dates to customers
  prior to or during the integration process," and not to share the integration with customers or display
  Rippling-branded collateral before beta is approved. That is a contractual constraint on how this work gets
  communicated, and worth repeating to whoever is asking for an ETA.
- **Partner support is an email address**, `partner.support@rippling.com`, named on every developer-portal page. When
  something in this skill does not match reality, that is the address to ask, and the answer is worth writing back
  into this file.
- **The third-party-integration-service restriction (REQ2) applies here, up front.** Do not submit an application that
  misdescribes what is being built. Whether an exception exists for a given platform is a conversation with Rippling's
  partnerships team, and a decision for a human on your side.

Everything in this section — eligibility, the application's contents, commercial and legal terms — is a business
decision. Collect the questions and hand them back (§Stop and ask).

## 3. Accounts: the partner company and the test company

Once approved, Rippling provisions **two** Rippling companies and emails the credentials:

- **The partner company** — where app listings live, where scopes and redirect URLs are configured, and where the
  client ID and secret are displayed. Sign in at `https://app.rippling.com` with the provided credentials, complete a
  short onboarding flow, then open the **Partner** app.
- **The test company** — a separate Rippling company you switch into to install and exercise your own app. This is
  Rippling's sandbox: **there is no self-serve sandbox**, and no way to get one before approval.

Practical notes that waste an afternoon each:

- **Profile switching is a real mechanic**, not a detail. You act as the partner company to build and as the test
  company to install, and the docs tell you to practise switching between them before you start. Rippling's
  deploy-notification email only works if the last tab you visited was the test company.
- **Two-factor setup and reset are documented partner-company procedures**, so expect human-only steps.
- **The first sandbox deploy can take up to 30 minutes.** Subsequent syncs take seconds. Do not interpret the first
  wait as a failure.

**Human-only steps** — receiving the onboarding email, first login, two-factor enrolment, accepting terms, submitting
the review forms — hand back rather than looping on them.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Give
the user an ordered click path with the literal values to paste — the redirect URLs from §5 and the scope strings from
§6 — and continue once they report back with the client ID.

## 4. Create and configure the app listing

In the partner company: **Create a new listing**, name it, then open the **listing version**. An app listing can carry
many versions; versions are how you stage changes, and they can be submitted for review, renamed, duplicated and
deleted without affecting customers.

Rippling's own advice is to "complete the OAuth connection with a light-weight app listing to start and then add
capabilities." The minimum that makes an install work:

| Setting | Where | Why it matters |
| --- | --- | --- |
| Display name | General → Identifiers | What customers see in the App Shop; also appears in the install and authorize URLs |
| Main category | Listing Information → Categories | Required before install |
| Icon | Listing Information → Icons | Required before install |
| Default Redirect URL | Integration → Redirect | The only redirect used for Rippling-initiated installs (§5) |
| Scopes | Integration → Scopes | Determines every endpoint you may call (§6) |

The **Integration → Credentials** tab is where the client ID and secret appear, split into **Sandbox** and
**Production** sub-tabs (§9). The **Testing** tab is where you deploy to the test company and track beta testers.

Two FAQ answers from Rippling worth keeping: IP allowlisting is not required for an app listing, though Rippling
publishes its IP ranges at `https://www.rippling.com/ip-ranges`; and listing assets (copy, images, promotions) can be
updated at any time directly in the partner company.

## 5. Redirect URLs

Register **every** callback host your platform serves. For Unified.to these are one per data center; confirm the
current list with the platform owner rather than assuming:

```
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
```

How Rippling treats them, which is unusual enough to read twice:

- **One is privileged.** The **Default Redirect URL** is "the URL that Rippling redirects to anytime an app
  installation starts from Rippling." Every Rippling-initiated install — the flow Rippling *requires* you to support,
  per REQ7 — uses that one and only that one.
- **Additional redirect URLs exist but are only reachable from the partner-initiated flow**, and only when explicitly
  passed: "Rippling will only redirect to the additional URLs during the partner-initiated installation flow and if
  you explicitly pass in the URL as a parameter." The parameter form is
  `https://app.rippling.com/apps/PLATFORM/{APPNAME}/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={URL}`.
- **A URL passed as a parameter is ignored unless it is already configured.** "Rippling will not redirect to any URL
  that is passed as a parameter unless it has been configured as an additional redirect URL in your app listing."
- **`http` is refused**; Rippling's own suggestion for local work is a tunnelling service.
- **The exchange must echo it.** `redirect_uri` is a required parameter on the token request and "must match the
  redirect URL you had provided to Rippling."

**The consequence for a multi-region platform is structural, and worth settling before anything else:** only one of
your four hosts can be the default. Either every Rippling-initiated install lands on one region's callback and is
routed onward from there, or each region needs its own app listing — which means its own client ID and its own App
Shop listing. That is an architecture decision for a human, not something to improvise (§Stop and ask).

## 6. Scopes — and why per-customer scope selection does not exist here

Scopes are managed per listing version under **Integration → Scopes**. "By default, your app listing will start with
no scopes." You add them from a categorised picker, and each one shows a status.

**The rule that breaks the usual design:** "Rippling API scopes are configurable within each app listing. **Scopes do
not vary by customer** for your app listing. Customers cannot choose to turn off scope ABC but turn on scope DEF when
they install your app listing. As part of every app installation flow, each customer admin must provide consent and
authorize **all** of the scopes that your app listing requires."

So a design that computes a per-connection scope string from the objects a customer enabled has nothing to attach to
on Rippling. The listing's scope set *is* the grant. Pick the minimum set that covers every object you intend to
support, and remember that widening it later forces **every existing customer to reauthorize** (§1).

**Public vs private scopes.** Public scopes are "automatically approved and then added to your app listing." Private
scopes "require an approval from Rippling before being added," and the request form demands a business case:
"Reasons must be a comprehensively documented business case. Please capture why your app listing needs this data, what
will you do with this data, and what value will this scope add to the customers." Rippling adds: "Access to Rippling's
private API scopes is extremely limited." Which scopes are private is not published — the picker shows it. Plan for a
refusal on anything sensitive, and never write a business case on the user's behalf.

**The catalog, as Rippling spells it.** REST scopes are `<resource>.read` and `<resource>.read-write`. A sample from
Rippling's published API Scope Reference, chosen for what an HRIS/ATS connector reaches for:

| Scope | Covers |
| --- | --- |
| `workers.read` | Workers — list and retrieve. Marked as granting sensitive data |
| `workers.custom-fields.read`, `workers.sensitive.personal.read` | Worker custom fields (Data Manager) and country-specific sensitive personal fields |
| `compensations.read`, `compensation-bands-details.read` | Compensation and bands. Sensitive |
| `companies.read` | Companies accessible to the authorized client |
| `departments.read` / `.read-write`, `teams.read`, `supergroups.read` / `.read-write`, `levels.read`, `titles.read` | Org structure |
| `work-locations.read` / `.read-write`, `legal-entities.read` | Locations and legal entities |
| `leave-requests.read` / `.read-write`, `leave-balances.read`, `leave-types.read`, `leave-accruals.read` | Time off |
| `time-entries.read` / `.read-write`, `time-cards.read`, `shiftassignments.read` / `.read-write`, `unassignedshifts.read` / `.read-write`, `schedules.read`, `shift-inputs.read` | Time and attendance |
| `candidates.read`, `candidate-applications.read` / `.read-write`, `job-requisitions.read` | ATS |
| `users.read`, `sso-me.read` | Users, and the current authenticated user's SSO information |
| `custom-objects.read` / `.read-write`, `custom-object-records.read` / `.read-write`, `custom-fields.read` | Platform custom data |

Traps in this catalog, each of which has cost someone a day:

- **Do not extrapolate a scope name from a sibling.** The grammar is mostly hyphenated-plural-dot-verb, but there are
  deliberate exceptions — `shiftassignments.read` and `unassignedshifts.read` have no hyphen, while
  `shift-inputs.read` does. Read the endpoint page or the published spec for the operation you actually call.
- **A scope that is not in the catalog is not a scope.** As of 2026-09-20 there is no `groups.read` in either the
  published scope reference or the published spec, and no `/groups` endpoint; group-shaped data comes from
  `/teams` and `/departments` under `teams.read` and `departments.read`. Any recorded scope string should be
  re-checked against the catalog rather than trusted because it once worked.
- **Some fields have their own scopes, and a missing one returns `null` rather than an error.** Rippling's 2026-09-01
  changelog adds `workers.pay-schedule.read`, `workers.work-schedule.read`, `workers.compensation-grade.read` and
  `workers.original-start-date.read`, noting "A request without the matching scope receives `null` for that field."
  Silent nulls are the most misdiagnosed Rippling symptom — see §12.
- **Some scopes need a parent to appear.** Rippling: "Certain scopes, such as `employee:read`, are only available once
  a child scope has been added."
- **Endpoints carry entitlement requirements on top of scopes.** The published spec tags operations with
  `Requires: API Tier 1` (the common case), and also `Global Payroll`, `Developer Program`, `Custom Apps Platform
  API`, `Worker Changes API`, `Functions API` and `Developer Base`. A customer without the entitlement fails even
  with a perfect scope set, and you cannot see their entitlements from your side.
- **A scope added to the listing is not in the sandbox app until you redeploy it.** Rippling's own FAQ: "I don't see a
  scope on my sandbox app but it is on my app listing" — redeploy.

`openid`, `profile` and `email` are OIDC-layer scopes used by the SSO flow (§8), not entries in the REST catalog.

## 7. Which API generation to build against

Getting this wrong is expensive and quiet, because the retired generation's documentation is still published and still
reads like current guidance.

| | Current — **REST API** | Legacy — **V1** |
| --- | --- | --- |
| Who | "All new App Shop partners", as of November 2025 | "Legacy partners" already on it |
| API host | `https://rest.ripplingapis.com` (Preview: `https://rest.ripplingpreviewapis.com`) | `https://api.rippling.com/platform/api/…` |
| Scope grammar | `workers.read`, `leave-requests.read-write` | `employee:name`, `company:teams` |
| Paging | forward-only, `limit` + `cursor` | `limit` + `offset` |
| Versioning | dated versions, `Rippling-Api-Version` header | — |
| Activity | endpoints and scopes added through September 2026 | maintained for existing partners |

Notes that matter in practice:

- **The OAuth endpoints are shared between generations.** Both exchange codes at `https://api.rippling.com/api/o/token/`.
  Only the data plane differs. So "our token exchange works" tells you nothing about which generation you are on.
- **The install guide itself mixes the two**: it documents the token endpoint and a V1-era "mark app installed" call
  on `api.rippling.com`, then demonstrates linking the connection with a REST call to
  `https://rest.ripplingapis.com/companies`. That is not an error to route around — it is the actual shape of the
  platform, and it means a working integration talks to two hosts.
- **Versioning is dated, and partners cannot pin.** Customers pick an API version when creating a token; for App Shop
  partners, "Reauthorization is necessary if a new version introduces breaking changes or alters scopes. Currently,
  partners cannot stay on the previous version once changes are adopted." Track the changelog.
- **Pagination is capped by contract as well as by the API.** REQ6: "Partners must use pagination… Pagination must not
  be set to more than 100." The REST API defaults to 50 and caps at 100 anyway.

## 8. The connect flow is an *install*, not a consent screen

This is the section that changes how a connector must be built.

On most platforms your product sends the customer to an authorize URL and receives a code. On Rippling the customer
starts in **their own Rippling account**, in the App Shop, and your OAuth leg is the middle of an installation that
must come back to Rippling to finish. Rippling requires it (REQ7, REQ9) and enforces it at review.

**The URLs:**

| Purpose | URL |
| --- | --- |
| App Shop listing (Rippling-initiated install starts here) | `https://app.rippling.com/app-shop/app/{APPNAME}` |
| Authorize step (partner-initiated install; also re-authorization) | `https://app.rippling.com/apps/PLATFORM/{APPNAME}/authorize` |
| Return screen (where the install must finish) | `https://app.rippling.com/apps/PLATFORM/{APPNAME}/return` |
| Token exchange and refresh | `https://api.rippling.com/api/o/token/` |
| OIDC authorize — **SSO only, not install** | `https://app.rippling.com/oidc/v1/authorize` |
| REST API base | `https://rest.ripplingapis.com` |

**The flow, with the parts that bite:**

1. The customer's admin opens your listing in the App Shop and starts the install. **They must be a Super Admin, Full
   Admin or App Admin**; anyone else cannot complete it, and the fix is a permission change in Rippling, not in your
   code.
2. Rippling redirects to your **Default Redirect URL** carrying three query parameters: `code`, `buy` (whether the
   user said they are buying a new account or connecting an existing one), and **`redirect_uri` — the URL on Rippling
   you must send the user back to.** Rippling's example:
   `…/callback?code=…&redirect_uri=https%3A%2F%2Fapp.rippling.com%2Fapps%2FPLATFORM%2FPortalTest%2Freturn&buy=false`.
   **A connect flow that drops that parameter and finishes on your own success page does not satisfy REQ9**, and the
   customer is left with an install Rippling considers unfinished.
3. **The code is valid for 300 seconds and is single-use.** Miss the window and the customer repeats the redirect step.
   Extract it server-side: "The authorization code should not be grabbed directly from the URL link after the
   redirect."
4. Exchange it: `POST https://api.rippling.com/api/o/token/`, form-encoded, with `grant_type=authorization_code`,
   `code` and `redirect_uri` in the body and **client ID and secret as an HTTP Basic header** — base64 of
   `client_id:client_secret`. Rippling documents Basic for the exchange *and* for refresh; it does not document
   client credentials in the body. PKCE does not appear anywhere in Rippling's documented flow.
5. Sign the user in on your side so you can associate the connection with their account, then **redirect them to the
   Rippling `redirect_uri` you were handed** to finish the install.
6. Optionally call Rippling's mark-app-installed endpoint to finalize (REQ11), and read the company (REQ10) so the
   Rippling company is matched to the right tenant on your side without the customer typing anything.

**One access token equals one Rippling company.** "If twelve Rippling companies have installed your app, then twelve
Access Tokens will need to be used." There is no cross-tenant token.

**The OIDC endpoint is for SSO after install, and only that.** If a connector points its authorize step at
`https://app.rippling.com/oidc/v1/authorize`, it is using the SSO leg for an install — contrary to Rippling's explicit
instruction — and is not running the App Shop install flow that REQ7 through REQ11 describe. Treat that as a finding
to raise, not a shortcut to keep.

## 9. Capture the credentials, and know what each token is worth

**Where they are.** Listing version → **Integration → Credentials**, with two tabs:

- **Sandbox** — "only display a client ID/client secret if you have already deployed your sandbox app listing." These
  install your sandbox app in your test company. "Customers should never use these credentials."
- **Production** — appears "once the app listing has been approved for beta testing." Every customer install uses
  this pair, including beta customers: "Can I use my sandbox app credentials for my beta customers? No, you cannot."

**They do not switch.** Rippling: "Will the credentials switch? No, they will not." Sandbox and production are two
permanent pairs on the same listing, and mixing them produces a specific error (§12).

**Token shape and lifetimes:**

| Token | What Rippling says |
| --- | --- |
| Authorization code | 300 seconds, single use |
| Access token | Read `expires_in` from the response and honour it — Rippling's own example shows `129600` (36 hours), and its OIDC page says tokens are "valid for three days or until refreshed". **Do not hardcode a lifetime; the docs are not consistent and the response is authoritative.** |
| Refresh token | **Single-use and rotating**: "Once a refresh token is used to retrieve another access token, that refresh token will be revoked." There is a **1-hour grace period** after first use in which it can be replayed. Store the new one from every refresh. |

**Refreshing** posts to the same token URL with `grant_type=refresh_token` and `refresh_token`, again with the Basic
header. REQ12 makes automatic refresh a listing requirement: "Customers should never see errors in the integration due
to access tokens not being refreshed."

**What revokes a connection outright** — worth building customer-facing messaging for, because none of it is your bug:

- The customer uninstalls your app. Rippling emits a `company.deleted` webhook, and calls start returning an error
  whose body reads **"The token has been revoked"**.
- **The admin who installed the app is terminated.** "All tokens associated with an employee are revoked when the
  employee is terminated… the person who has 'installed' the app must always be in the company."
- That admin loses the permissions the app needs, which produces a 403 rather than a 401.

**Recovery is always the same shape**: the customer re-installs or re-authorizes at
`https://app.rippling.com/apps/PLATFORM/{APPNAME}/authorize`, producing a fresh code and a fresh token pair. There is
no server-side way to revive a revoked pair.

**Rotation.** Rippling does not document a self-serve secret rotation for a listing's credentials. Treat a rotation
request as a partner-support conversation, and never rotate without a cutover plan and explicit go-ahead.

### The other credential: customer-created API tokens

Separately from partner OAuth, **a Rippling customer can mint their own API token** inside their own account —
`https://app.rippling.com/api-tokens/tokens`, or **Tools → Developer → API Tokens**. It is a bearer token against
`https://rest.ripplingapis.com`, created with a name, an API version and a scope selection. Facts that decide whether
it is usable:

- **It inherits its creator.** Access is "determined by two factors: the permission profile of the user who created
  the token" and the scopes chosen. A user cannot select a scope their profile does not allow, and "changes to a
  user's permissions automatically applies to all tokens they've created."
- **It is shown once.** "You cannot view the API token again."
- **It dies quietly.** "Automatically revoked or expired if the owner is terminated or the token remains unused for
  over 30 days." A revoked token returns `401`. The 30-day idle expiry catches low-traffic tenants.
- **Only the creator can edit its scopes**; token admins can change a token's name and API version but not its scopes.
- **It is not a substitute for partner OAuth on the App Shop path.** REQ2: "3rd-party vendors using API keys are not
  supported." This credential is for a customer's own internal integration.

## 10. What this platform's connector currently does — dated product facts

**As of 2026-09-20**, and stated as an observation to confirm with the connector's owner, because connector
configuration changes far more often than a vendor portal does:

- It supports **two credential modes** — an OAuth 2.0 authorization-code connection, and a customer-supplied API token
  sent as a bearer credential, with onboarding text pointing the customer at **Tools → Developer → API Tokens** and
  warning that the value is shown once. Note the API-token mode against REQ2 (§intro, §9).
- It targets the **current REST generation**: `https://rest.ripplingapis.com`, with REST-shaped paths (`/workers`,
  `/work-locations`, `/leave-requests`, `/shiftassignments`, `/candidates`, `/candidate-applications`,
  `/job-requisitions`, `/payroll-runs`, `/teams`, `/departments`) and Rippling's `order_by`, `limit` and cursor-style
  paging. That is the right generation.
- It sends the token exchange to **`https://app.rippling.com/api/o/token`** rather than the documented
  `https://api.rippling.com/api/o/token/`. Both hosts answered a credential-less `POST` with `400` on 2026-09-20, so
  the recorded one is live — but it is not the URL Rippling documents, and it is worth aligning rather than relying on
  an undocumented alias.
- It points its **authorize step at `https://app.rippling.com/oidc/v1/authorize`** — the OIDC SSO endpoint Rippling
  says must not be used for installs (§8). This is the connector fact most worth raising with its owner.
- It sends **client ID and secret as an HTTP Basic header** on both the exchange and the refresh, which matches
  Rippling's documentation, and sends **no PKCE**, which also matches (Rippling documents none).
- It carries **per-object scope sets** — `workers.read` and `leave-balances.read` for employees, `work-locations.read`
  / `.read-write` for locations, `shiftassignments.read` for shifts, `departments.read` for groups,
  `candidates.read`, `job-requisitions.read` and `candidate-applications.read` for ATS, `leave-requests.read` /
  `.read-write` for time off, and `payroll-runs.read` with `worker-payroll-records.read` for payslips — plus `openid`,
  `profile` and `email` on every set and `sso-me.read` on the login set. Against §6: per-object scope sets have no
  effect on Rippling, where the *listing* holds the grant; and one recorded string, `groups.read`, is **not** in
  Rippling's published catalog or spec, while `teams.read` — needed for the teams half of group data — is absent from
  the recorded sets.
- It ships **no shared platform-level Rippling OAuth credentials**: every workspace must supply its own client ID and
  secret, which in practice means its own Rippling partnership (§2).
- It records Rippling's **sandbox as not self-serve** and links the partner process page — consistent with §2 and §3.
- Its recorded **API-token onboarding path and filter grammar** (Rippling uses `ge`/`le` rather than `gte`/`lte` in
  filter expressions) match Rippling's current query-parameter documentation.

## 11. Review, requirements and publishing

**The listing requirements are contractual, and they are broader than OAuth.** A listed Rippling app must, by default:

- Support **SSO** — SAML or OIDC (REQ3), with SAML metadata fetched automatically per install and **unique to every
  customer install**, including re-installs by the same company (REQ20).
- Support **user management** (REQ4): a webhook listener for access-granted and access-removed events (~5-minute lag),
  a scheduled sync at least every 24 hours, a manual sync button in your product, and **automated employee matching**
  so admins are not asked to map users by hand (REQ13–REQ17).
- Support the **Rippling-initiated install** that ends back in Rippling (REQ7, REQ9), with **no manual data entry** to
  establish the connection (REQ10).
- **Refresh tokens automatically** (REQ12) and **paginate** at no more than 100 (REQ6).
- If OIDC SSO is enabled on the listing, **every customer must configure it** during setup — "unlike SAML SSO, OIDC
  SSO is not optional."

**The review path:**

1. **Integration Review form** → approval for **beta**. The form covers a general requirements checklist, installation
   flow checklists and videos, SSO, user management, data sync, support and maintenance, a Rippling test account, and
   your beta testers. Once submitted, the only way to edit the listing is to cancel the review and revert to draft
   (which does not affect existing installs).
2. Production credentials appear; **at least three outside companies** (beyond your test company) must install the
   production listing.
3. **Launch Review form** → approval to publish. It asks for integration documentation (required) and optionally a
   sales demo.
4. **Publish** — the listing becomes discoverable in the App Shop. Publishing does not disturb beta installs.

Beta is production in all but discoverability: "Beta testers interacting with your app will experience the same
functionality as real customers would after the app is published."

**Limits to design against** (§Platform state): 300 requests per IP per sliding 10-second window with a 10-second
total rejection penalty; pagination 50 default / 100 max; 64 filter nodes; a maximum expand depth. On `429`, "stop
sending requests temporarily," back off exponentially, and do not retry immediately — the penalty window resets from
zero only after it elapses. Rippling reserves the right to change limits at any time.

Everything about the review forms, the three beta companies, security and legal claims is a business decision. Do not
answer any of it on the user's behalf.

## 12. Verify end-to-end

"The token came back" proves very little here. Test the customer's path.

1. **Install the sandbox app in the test company** from the App Shop link on the sandbox-testing tab — not by
   hand-rolling an authorize URL. Confirm the listing has a category, an icon, a default redirect URL and its scopes,
   and that the sandbox app has finished deploying.
2. **Run the full install**, including the return leg: confirm your flow reads the `redirect_uri` parameter Rippling
   sent and sends the admin back to Rippling's return screen (§8). An install that ends on your own page is a review
   failure waiting to happen.
3. **Exchange within 300 seconds**, with Basic auth, and confirm the response carries a refresh token.
4. **Refresh, then refresh again using the token returned by the first refresh.** Two minutes of work for a failure
   that otherwise appears a day later (§9).
5. **Make one read per scope family** against `https://rest.ripplingapis.com`, and **compare the fields you get with
   the fields you expect** — a missing field-level scope returns `null`, not an error (§6).
6. **Page past the first page** with a cursor Rippling actually returned, at `limit` ≤ 100.
7. **Try installing as a non-admin** if any customer might. Expect failure, and design the message for it.
8. **Uninstall from the test company** and confirm your side handles the revoked-token error and the `company.deleted`
   webhook by resetting state for a future re-install.

| Symptom | Cause |
| --- | --- |
| `401 Invalid Client` when installing your sandbox app in the test company | Production credentials used against the sandbox app, or the reverse. The two pairs never switch (§9) |
| No client ID or secret visible on the Credentials tab | Sandbox: the sandbox app has not been deployed. Production: the listing has not been approved for beta (§9) |
| A scope is on the listing but the sandbox app does not have it | The sandbox app was not redeployed after the scope was added (§6) |
| `403` with body "The token has been revoked" | The customer uninstalled the app; also emitted as a `company.deleted` webhook. Reset state and await re-install (§9) |
| `401` on a connection that worked for months | The admin who installed the app was terminated — all their tokens are revoked. Another admin must re-authorize (§9) |
| `403` right after a successful install | The installing admin lacks the permissions the app's scopes imply. Re-installing as the same user yields the same 403; a higher-permissioned admin must install (§8) |
| Fields come back `null` for everyone, no error | A field-level scope is missing — Rippling returns `null` rather than failing (§6) |
| A whole endpoint `403`s for some customers only | An entitlement gate (`API Tier 1`, `Global Payroll`, `Developer Program`, …), or the customer lacks the Identity & Access Management package (§6, §2) |
| Token exchange `404`s | The endpoint from the published spec (`app.rippling.com/o/token`) was used; it returned 404 on 2026-09-20. Use `https://api.rippling.com/api/o/token/` (§Platform state) |
| Authorization code rejected as invalid | Older than 300 seconds, or already used once (§8) |
| Second refresh fails although the first succeeded | The rotated refresh token is not being stored; the 1-hour grace period masked it once (§9) |
| Redirect lands somewhere unexpected on a Rippling-initiated install | Only the **Default** Redirect URL is used for Rippling-initiated installs; additional URLs work solely in the partner-initiated flow and only when passed explicitly (§5) |
| A redirect URL passed as a parameter is ignored | It is not configured on the listing. Rippling only redirects to configured URLs (§5) |
| Cannot add a redirect URL | It is `http`. Rippling refuses plain `http` (§5) |
| Everything fails for ~10 seconds under load | The 300-requests-per-IP-per-10-seconds burst threshold and its penalty window (§11) |
| Endpoint or scope names from an older runbook are rejected | V1 grammar against the REST API, or the reverse (§7) |
| The customer cannot find your app in the App Shop | Still in beta — reachable only by direct production URL — or the customer lacks the Identity & Access Management package (§11, §2) |

## 13. Hand off — never commit the secret

- **Do not** write a client secret or a customer API token into source control, a test, a fixture, a committed `.env`,
  a ticket, a PR body or a chat channel. Values go to the human running this, for their secret store or console.
- **Do not paste a live token into a support email.** Rippling's troubleshooting guide asks for what you were doing,
  when, the status code and the response's request identifier — send those, redact the credential.
- If a code change is needed (a callback host, a scope string, the token URL), keep it credential-free and say plainly
  what the human must set out of band.
- Report a secret once so it can be pasted into the secret store, say clearly that it is now in the transcript and can
  be rotated, then move on.
- Close with: partnership status and who owns the partner company; the app listing name and the `{APPNAME}` that
  appears in the install, authorize and return URLs; which credential pair (sandbox or production) and its client ID;
  where the secret was delivered; the exact Default Redirect URL registered and any additional ones; the exact scope
  strings on the listing and any private-scope requests still pending; which API generation the integration targets;
  the Super/Full/App Admin requirement to pass to customers; the refresh-rotation behaviour your client must honour;
  and whatever is still waiting on Rippling.

## Stop and ask

Hand back to a human rather than guessing when:

- **There is no Rippling partnership** and the ask is OAuth credentials — say so in the first reply rather than
  starting something that cannot finish (§2).
- **The integration is a third-party integration service connecting many customers' Rippling accounts.** REQ2 and the
  Developer Terms of Use address that model directly, naming only two sanctioned aggregators. This is a partnerships
  conversation and a legal review, not a form to fill in (§intro).
- The App Shop application, Integration Review or Launch Review needs business, security or compliance claims, a
  customer count, or a commitment to a date (REQ5 forbids the last one).
- **Four regional callbacks must coexist** and only one can be the Default Redirect URL — one listing routed through a
  single callback, or one listing per region, is an architecture decision (§5).
- A **private scope** is needed: the justification is a business statement, and Rippling says access is "extremely
  limited" (§6).
- Someone proposes creating a **new listing** for a live integration, which forces every customer to install again (§1).
- A customer's admin cannot complete an install, or data is missing for one tenant only — that is Rippling permissions
  or entitlements, changed on their side (§8, §12).
- Rippling's documentation contradicts itself on something load-bearing — token URL, token lifetime, scope name — and
  the answer must come from `partner.support@rippling.com` rather than from a guess (§Platform state).
- The portal or docs no longer match the **Platform state** section above.

Note that this skill was written without browser automation: every fact came from fetching Rippling's public
documentation and probing its hosts. A run that also has no browser should hand click paths and literal values to the
user rather than simulating a portal session.

## References

Verified to return HTTP 200 on 2026-09-20. Note that `developer.rippling.com` and `app.rippling.com` are single-page
apps that answer 200 for any path — the pages below were additionally confirmed to exist in the documentation site's
own route manifest.

- Developer home — https://developer.rippling.com/
- Integration Checklist (the partner process, end to end) — https://developer.rippling.com/documentation/developer-portal/getting-started/process
- Partner Requirements (REQ1–REQ24, incl. REQ2 on third-party integration services) — https://developer.rippling.com/documentation/developer-portal/getting-started/requirements
- API Access (which generation new partners use) — https://developer.rippling.com/documentation/developer-portal/getting-started/api
- Pricing and packaging (Identity & Access Management requirement) — https://developer.rippling.com/documentation/developer-portal/getting-started/pricing
- Register your Rippling Account (partner + test company onboarding) — https://developer.rippling.com/documentation/developer-portal/v2-guides/registration
- Installation (OAuth) — install flow, token exchange, refresh, lifetimes — https://developer.rippling.com/documentation/developer-portal/v2-guides/installation
- OAuth Credentials (sandbox vs production pairs) — https://developer.rippling.com/documentation/developer-portal/v2-guides/oauth
- Redirect URLs (default vs additional) — https://developer.rippling.com/documentation/developer-portal/v2-guides/redirect
- Scopes (listing-level grant, public vs private) — https://developer.rippling.com/documentation/developer-portal/v2-guides/scopes
- OpenID Connect SSO (OIDC authorize endpoint, and when not to use it) — https://developer.rippling.com/documentation/developer-portal/v2-guides/openid
- Errors (401/403/500/504, revoked tokens) — https://developer.rippling.com/documentation/developer-portal/v2-guides/errors
- Webhooks — https://developer.rippling.com/documentation/developer-portal/v2-guides/webhooks
- Create an App Listing — https://developer.rippling.com/documentation/developer-portal/creating-publishing/create
- Deploy an App Listing (sandbox deploy; 30-minute first deploy) — https://developer.rippling.com/documentation/developer-portal/creating-publishing/deploy
- Install an App Listing (with and without Postman) — https://developer.rippling.com/documentation/developer-portal/creating-publishing/install
- Manage Beta Testing (Integration Review, three outside companies, Launch Review) — https://developer.rippling.com/documentation/developer-portal/creating-publishing/testing
- Publish an App Listing — https://developer.rippling.com/documentation/developer-portal/creating-publishing/publish
- Legacy API Reference (V1 partners) — https://developer.rippling.com/documentation/developer-portal/legacy-api/api
- V1 scope list (the older grammar, for contrast) — https://developer.rippling.com/documentation/base-api/scopes
- REST API Scope Reference (the current catalog) — https://developer.rippling.com/documentation/rest-api/essentials/permissions
- API Tokens and Permissions (customer-created tokens) — https://developer.rippling.com/documentation/rest-api/essentials/api-tokens
- API Usage and Rate Limits — https://developer.rippling.com/documentation/rest-api/essentials/api-limits
- API Errors and Troubleshooting — https://developer.rippling.com/documentation/rest-api/essentials/troubleshooting
- API Versioning and Upgrades — https://developer.rippling.com/documentation/rest-api/essentials/versioning
- REST API changelog — https://developer.rippling.com/documentation/rest-api/essentials/changelog
- Published REST OpenAPI document (scope catalog; note its OAuth URLs are wrong — §Platform state) — https://developer.rippling.com/docs/rest-beta.yaml
- App Shop listing application (service desk) — https://rippling-public.atlassian.net/servicedesk/customer/portal/3
- Rippling Developer Terms of Use (renders client-side; read in a browser) — https://app.rippling.com/developer/tos
- Rippling IP ranges — https://www.rippling.com/ip-ranges
- Rippling App Shop — https://www.rippling.com/app-shop
- Rippling partners overview — https://www.rippling.com/partners
- Admin permissions for installing apps (Help Center) — https://help.rippling.com/article?articleId=508625197432
