---
name: quickbooks-oauth-app
description: Creates or signs in to an Intuit Developer account and registers a QuickBooks Online app to obtain OAuth2 client ID and client secret — covering the sandbox/production key split, redirect URIs, the `com.intuit.quickbooks.*` scopes, the realmId returned on the callback, rotating refresh tokens, and the app assessment questionnaire that gates production access. Use when asked to get QuickBooks or Intuit OAuth credentials, set up an Intuit developer account, create a QuickBooks app, go live with production keys, rotate a QuickBooks client secret, or fix a QuickBooks OAuth error like `invalid_grant`, `invalid_scope`, a rejected redirect_uri, or a 403 `ApplicationAuthorizationFailed`. For any other vendor's developer portal, use that vendor's skill instead.
---

# QuickBooks Online (Intuit) OAuth2 App Registration

Get a working Intuit OAuth2 client — a developer account, an app, redirect URIs, scopes, and the client ID and
secret — for a platform that connects customer QuickBooks Online companies on behalf of many customers.

Three things here cost real time. First, **one Intuit app carries two credential pairs** — Development and
Production — and they are not interchangeable: sandbox keys never reach a production company, and the failure
surfaces as a 403 at the first API call, not at registration. Second, **production keys are a review gate**, not a
button: Intuit requires an app assessment questionnaire from every app that touches production data, listed on the
app store or not, and a "Not approved" result can leave you unable to resubmit. Third, **Intuit returns a `realmId`
on the callback and rotates refresh tokens**; a client that drops either one authorizes fine and then dies quietly —
the first at the first API call, the second a day or two later.

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **App name**, logo, description | Shown on the Intuit consent screen |
| **Intuit developer account / workspace** | Existing, or a new signup (§2) |
| **New app or an edit to an existing one?** | New keys orphan every existing connection (§1) |
| **Redirect URIs** | Every callback host, per environment (§5) |
| **Which surface**: Accounting, Payments, or both | Decides scopes and connector (§6, §11) |
| **EULA URL and Privacy Policy URL** | Required before production keys are issued (§7) |
| **Host domain, Launch / Disconnect / Connect URLs** | Portal app-settings fields (§4, §7) |
| **Target industries / regulated-industry answer** | Asked in the questionnaire; a wrong answer can fail it (§7) |
| **Does the client store the rotated refresh token and the realmId?** | Blocking — see §9 |

## Quick Start

1. Confirm a **new app** is actually needed — existing connections are bound to the current client ID (§1).
2. Sign in to, or create, the Intuit Developer account and workspace (§2).
3. Read the dated platform notes before trusting any older runbook (§3).
4. Create the app and pick its product line and scopes (§4).
5. Understand the **two key pairs** before touching anything else (§4 → §5).
6. Add **every** redirect URI, under **both** Development and Production (§5).
7. Request the narrow scope set the connector actually calls (§6).
8. Work the **going-live review** — questionnaire, URLs, industries — to get production keys (§7).
9. Capture client ID and secret per environment (§8).
10. Verify a real authorize → callback → token → refresh → read round trip (§10).
11. Hand the credentials over — never commit them (§11).

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

- **Authorize and token endpoints are the same for sandbox and production.** Confirmed live from Intuit's own OIDC
  discovery documents on 2026-09-20: both `openid_configuration` and `openid_sandbox_configuration` return
  `authorization_endpoint` `https://appcenter.intuit.com/connect/oauth2`, `token_endpoint`
  `https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer`, and `revocation_endpoint`
  `https://developer.api.intuit.com/v2/oauth2/tokens/revoke`. Only `userinfo_endpoint` differs
  (`accounts.platform.intuit.com` vs `sandbox-accounts.platform.intuit.com`). **The environment is selected by which
  key pair you send, not by which URL you call** — which is exactly why mixing them is so easy and so silent.
- **Production keys are gated behind the app assessment questionnaire.** Every app with connections to production
  QuickBooks Online companies must submit it, private/unlisted included. It is a self-assessment plus attestations,
  not a code review — but it is a gate, and it is separate from app-store review.
- **App-store listing is a different, deeper process**: technical, legal, security and marketing review, repeated
  annually. Intuit also states that apps **not** on the app store must meet the store's security requirements once
  they exceed **500 connections**, and that it checks all apps annually. A multi-tenant connector crosses that line.
- **Minor version 75 is both the default and the floor.** Support for minor versions 1–74 of the Accounting API was
  discontinued on 2025-08-01; a lower `minorversion` value is ignored and 75 is served.
- **Scopes are additive-only on an app**: you can add a scope, you cannot remove one.
- **Up to 25 redirect URIs per app.** Production URIs must be `https`, and cannot be `localhost` or an IP address.
- **Sandbox**: up to 10 sandbox companies per developer account, valid for two years, not renewable — create a new
  one when it expires, and ignore any prompt to enter payment details.
- **Webhooks are not registerable by API.** The webhook endpoint URL and entity subscriptions are configured by hand
  in the developer portal, per environment.

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

## 1. Decide: reuse the existing app, or register a new one

A new app means a **new client ID, and every existing customer connection is bound to the old one** — every customer
would have to re-authorize, and each re-authorization is a company-admin action you cannot perform for them.

Reuse the existing app for: adding a redirect URI, adding a scope, rotating a compromised secret, filling in app
settings for the going-live review, or diagnosing an authorization failure.

Register a **new** app only when the user explicitly wants one: a replacement for a compromised app, a separate app
for a different product or surface (Accounting vs Payments vs Desktop), or a deliberate migration. Say which path
you are taking before you touch the portal.

## 2. Account: sign in or sign up

- **Developer portal**: `https://developer.intuit.com/` — sign in, then **My Hub → App dashboard**. Workspaces at
  `https://developer.intuit.com/workspaces`.
- **Sandbox companies**: **My Hub → Sandboxes**, or `https://developer.intuit.com/sandbox-companies`. A developer
  account is provisioned with a sandbox company; you can add more up to the cap.
- **Team access**: apps belong to a workspace and everyone on the app **Team** can see the app, its keys and the
  assessment questionnaire. Add the people who will maintain it before you need them at 2am.

Hand control back to the user for anything only a human can do: account signup and email verification, CAPTCHA,
MFA enrollment, accepting Intuit's Developer Terms of Service, and the assessment questionnaire's attestations. Do
not retry a blocked step in a loop.

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

## 3. Create the app

From the app dashboard, create an app and choose its **product line**. For a QuickBooks Online connector that is
**"QuickBooks Online and Payments"**; the portal also offers ProConnect Tax and QuickBooks Desktop lines, which are
different products with different scopes.

The scope picker at creation offers **Accounting** and **Payments (US only)**. Pick what the connector actually
calls — you can add later, you cannot remove (§0 platform state). Beta-gated fine-grained scopes (retirement /
payroll, data-integration) appear only for allowlisted apps; if the user asks for one that is not offered, that is a
request to Intuit, not a checkbox.

Then work through **Settings** on the app, because several of these fields are what the going-live review checks:

| Setting | What it is |
| --- | --- |
| **Keys & credentials** | Client ID and secret, with a Development / Production toggle (§4) |
| **Redirect URIs** | Per environment; Development and Production are separate lists (§5) |
| **App URLs** | Host domain, Launch URL, Disconnect URL, Connect/Reconnect URL — also per environment |
| **App terms of service** | EULA URL and Privacy Policy URL — required for production keys |
| **App categories** | e.g. Accounting |
| **Accepted connections** | Which countries may connect |
| **Regulated industries** | Answer this honestly and *before* the questionnaire — see §7 |

Basic app info (name and logo) is what renders on the consent screen. Developers have reported the App Center
showing "Sorry, but *undefined* didn't connect" when app profile data has not resolved — a blank app name on a
consent screen is a portal-side symptom, not a bug in your authorize URL.

## 4. Sandbox vs production keys

**This is the section that catches people.** One app, two independent credential pairs:

| | Development keys | Production keys |
| --- | --- | --- |
| Connect to | Sandbox companies only | Live QuickBooks Online companies only |
| API host | `https://sandbox-quickbooks.api.intuit.com` | `https://quickbooks.api.intuit.com` |
| Userinfo host | `sandbox-accounts.platform.intuit.com` | `accounts.platform.intuit.com` |
| Authorize / token / revoke | **Identical to production** | **Identical to development** |
| Redirect URIs | Its own list; may include `localhost` (http allowed) | Its own list; `https` only, no `localhost`, no IPs |
| Available | Immediately | Only after the going-live review (§7) |

Rules that follow from that table:

- **Use a pair as a pair.** A Development client ID with a Production secret, or vice versa, fails in ways that read
  like an Intuit outage.
- **Sandbox keys against a production company (or the reverse) is the classic 403.** Intuit's own guidance: a 403
  usually means the key type does not match the company type. Expect `ApplicationAuthorizationFailed` / error code
  `003100` on every data call while OAuth itself looks perfectly healthy.
- **Because the authorize URL is identical**, a misconfigured environment gets all the way through consent before
  anything goes wrong. Do not treat "the user saw the consent screen" as evidence the environment is right.
- **The API host is the tell.** Whatever picks the base URL — config, environment flag, connection record — must be
  driven by the same switch that picks the key pair, or the two drift apart.
- Development credentials remain useful after go-live; keep both pairs, clearly labelled, and never let a sandbox
  value reach a production config.

## 5. Redirect URIs

Register **every** callback host your platform serves, in **both** the Development and Production lists. 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
```

Notes:

- **Exact match, and Intuit means it**: the `https` scheme, the same case, and the same trailing `/`. No
  subdirectory matching, no trailing-slash forgiveness. A mismatch is rejected at connect time, with a message
  telling you to list it on the app's keys tab — which it already is, just not byte-identically.
- IP addresses are never allowed. `localhost` is allowed for **sandbox only**, and without HTTPS there.
- Up to **25** URIs per app. Adding a data center later is an edit to this list, not a new app.
- The portal pre-populates the OAuth Playground's redirect URL. Leave it — it is what makes §10's playground check
  possible — but do not mistake it for your callback.
- **Confirm the save persisted.** Reload the page and re-read the list. Silent save failures on production settings
  have been reported; a URI that did not stick looks exactly like a client sending the wrong value.

## 6. Scopes

Space-delimited in the authorize URL. The strings, exactly as Intuit spells them:

| Scope | Why |
| --- | --- |
| `com.intuit.quickbooks.accounting` | The QuickBooks Online Accounting API — the connector's data scope |
| `com.intuit.quickbooks.payment` | The QuickBooks Payments API (US only) — a **separate** surface, see §11 |
| `openid` | OpenID Connect processing; required to receive an ID token |
| `profile` `email` `phone` `address` | Identity claims only — no accounting data |
| `com.intuit.quickbooks.payroll*` | Payroll APIs, allowlisted beta apps only — do not request speculatively |

Points worth holding on to:

- **Identity scopes are not data scopes.** `openid email profile` gets you a signed-in user and a userinfo call; it
  gets you nothing from the Accounting API. A token minted with only identity scopes produces 403s on every entity
  call — one of the two shapes of "auth worked, API didn't".
- The authorize request must also carry `response_type=code`, `client_id`, `redirect_uri` and a `state` you verify
  on the way back (Intuit documents `state` as required, for CSRF).
- A scope the app does not have enabled comes back as `invalid_scope` **on the authorize URL**, before consent.
  GraphQL and beta scopes that are not on the app's permissions page cannot be requested at all.
- Scopes are additive on the app but **each authorization is bound to the scopes it was granted with**. Adding a
  scope to the app does not upgrade existing tokens; customers re-authorize to pick it up.

## 7. Going live: the review that issues production keys

Production keys are not available on demand. Two distinct processes exist and they are frequently confused:

**A. The app assessment questionnaire — required for everyone.**

- **Where**: app dashboard → **Production Settings** tab → **App assessment questionnaire** in the left nav.
- **Who**: every developer whose app connects to one or more production QuickBooks Online companies. Explicitly
  including private, unlisted apps — Intuit's FAQ answers that question directly, with "Yes".
- **What it asks**: attestations that you meet Intuit's platform requirements; information about how the app uses
  the QuickBooks platform; and extra questions if the app operates in certain industries. Intuit's help content puts
  it at roughly **40 minutes** once you have the answers collected. Status typically appears on the app dashboard
  within minutes of submission.
- **Mechanics that bite**: answers must be **Saved** explicitly or they are lost on navigation; you can edit freely
  until you **Submit**, and **not at all afterwards**; resubmission is only offered when Intuit asks you to make
  changes. Developers have reported a "Completed / Not approved" result with no stated reason and no resubmit
  option, requiring a support ticket to reopen. **Get the answers right the first time.**
- **Regulated industries is part of this.** An app that only reads accounting data should say so. A stray
  regulated-industry selection has been reported as the thing that failed an otherwise fine assessment.
- **Before you can submit**, fill in the app's **EULA URL** and **Privacy Policy URL** under Production, plus host
  domain and the Launch / Disconnect / Connect URLs, and select target industries. Intuit's go-live page routes you
  to **Production → Keys & OAuth** for the keys themselves.

**B. App-store listing review — only if you want to be listed.** Technical, legal, security and marketing review,
run annually thereafter, with a third-party security vendor contacting you twelve months after you pass. A private
connector does **not** need this.

**But note the 500-connection rule.** Intuit's security requirements state that apps *not* published on the app
store must still meet those requirements once they exceed **500 connections**, and that Intuit checks all apps
annually. A connector that succeeds will cross that threshold. Raise it with the user early; it is a security and
compliance commitment, not a portal setting.

**Stop and ask, do not guess.** The questionnaire asks about data handling, retention, encryption, breach process,
sub-processors and industry exposure. Those are the platform owner's answers, not yours. Collect them; do not
compose them.

## 8. Capture the credentials

App dashboard → your app → **Keys & credentials**, then toggle **Development** or **Production**.

Capture, per environment:

- **Client ID** and **Client secret** — as a matched pair, labelled with their environment
- **Authorize URL**: `https://appcenter.intuit.com/connect/oauth2`
- **Token URL**: `https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer` (HTTP Basic with
  `client_id:client_secret`, `application/x-www-form-urlencoded` body)
- **Revoke URL**: `https://developer.api.intuit.com/v2/oauth2/tokens/revoke`
- **API base URL** for that environment (see the §4 table)
- The app's **App ID**, for support tickets

Unlike portals that show a secret once, Intuit's Keys & credentials page displays the pair for re-reading, and the
secret can be rotated there. Rotation invalidates the old secret: existing access tokens live out their hour, but
every token exchange and refresh fails until the new secret is deployed. Never rotate without a go-ahead and a
cutover plan.

Report the secret once so the user can paste it into their secret store, say plainly that it is now in the
transcript and can be rotated, then move on.

## 9. Token lifetimes, rotation, and the realmId

Four facts, and a connector that misses any one of them breaks later rather than now.

| Fact | Consequence |
| --- | --- |
| **Access token: 60 minutes** (`expires_in` 3600) | Refresh proactively; a stale token is a 401 on every call |
| **Refresh token: documented as up to 100 days** — the sample token response carries `x_refresh_token_expires_in` `8726400` (101 days) | This is an **absolute** clock, not idle time. Past it, the customer must re-authorize |
| **Refresh tokens rotate** — the value can change roughly every 24–26 hours | Store the **new** refresh token from every token response. Reusing an old one returns `invalid_grant` |
| **`realmId` arrives on the callback**, not in the token response | Store it with the credentials. Every Accounting API path is `/v3/company/{realmId}/...` |

More on each:

- **Rotation is the silent killer.** A client that persists only the first refresh token works for a day, then every
  refresh fails with `invalid_grant` and the connection is dead with no user-visible event. This is the single most
  common way a QuickBooks integration dies. Treat "we store the token we got at connect" as a defect, not a design.
- **The 100-day clock keeps running even if you refresh.** Refreshing mints a new refresh token with a fresh window,
  so an actively-used connection stays alive indefinitely — but a connection nobody syncs for three months does not.
  Idle connections are the ones that expire.
- **`realmId` identifies the QuickBooks company**, and is returned on the redirect alongside `code` and `state`.
  Intuit's own note: if the user is not already signed in to QBO with realm context, the realmId may not come back
  in the redirect at all — handle its absence as a failed connect, not as a nullable field.
- **One token spans exactly one company. Never more.** A customer with five QuickBooks companies authorizes five
  times and you store five (token, realmId) pairs. There is no "list my companies" call on an accounting token, and
  reusing one company's token against another realm is an authorization error. Intuit shows the company picker
  before handing control back, so which company you got is *their* choice — read it from `realmId`, do not infer it.
- **Disconnect invalidates both tokens.** A user disconnecting your app inside QuickBooks (or you calling the revoke
  endpoint) kills the access and refresh tokens immediately; the next call is an auth failure and the only recovery
  is a fresh authorization. Reconnecting is a full new consent, and produces a new refresh token — and, if they
  picked a different company, a different realmId.
- **Also worth pinning: the API minor version.** `?minorversion=75` is the current default and floor; versions 1–74
  were retired on 2025-08-01. Pinning it explicitly makes the response shape a decision rather than a default that
  moves under you.

## 10. Verify end-to-end

Authorizing against a sandbox proves the sandbox pair works, and nothing about production. Test both, in order.

1. **Sandbox**: run the full authorize → callback → token exchange with the Development pair against a sandbox
   company. Intuit's OAuth 2.0 Playground is the fastest way to prove the keys and redirect URI in isolation — if
   the playground works and your app does not, the fault is in your client, not the registration.
2. Confirm the callback carried **`code`, `state` and `realmId`**, and that `state` matches what you sent.
3. Confirm the token response carried an `access_token`, a `refresh_token`, `expires_in` 3600 and
   `x_refresh_token_expires_in`.
4. Make one read call against `https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/companyinfo/{realmId}`
   with `minorversion=75`.
5. **Force a refresh, then force a second refresh using the token the first one returned.** This is the step that
   catches a client ignoring rotation. Nothing else catches it before production does.
6. **Production**: repeat 1–5 with the Production pair against a real QuickBooks Online company, authorized by a
   company admin who is not you where possible.

| Symptom | Cause |
| --- | --- |
| `redirect_uri` rejected as invalid | Not byte-identical to a registered value, or registered in the other environment's list (§5) |
| `invalid_scope` on the authorize URL | Scope not enabled on the app, or a beta/allowlisted scope (§6) |
| Consent screen shows the app name as `undefined` | App profile data not resolving in the App Center — portal-side; open a support ticket with the App ID |
| Consent succeeds, every API call 403 / `ApplicationAuthorizationFailed` / `003100` | Wrong environment key pair for the company type, or wrong API host, or only identity scopes granted (§4, §6) |
| Everything works, then all calls 401 after an hour | Access token expired; refresh not wired up (§9) |
| Works for a day, then every refresh returns `invalid_grant` | Rotated refresh token not stored (§9) |
| Connection dies after a quiet few months | 100-day absolute refresh-token expiry; customer must re-authorize (§9) |
| Auth fine, but calls 404 or hit the wrong company | `realmId` not stored, or a token used against another realm (§9) |
| Customer's second admin connects and the first connection stops working | Intuit binds user → app → company; a later admin authorization can displace the earlier one. Store and reuse the company's tokens rather than per-admin ones |
| Redirect URI or app URL "saves" but is gone on reload | Portal-side persistence failure — re-read after saving, then open a support ticket with the App ID (§5) |
| Production tab shows the same keys as Development, or "Get production keys" loops | Production keys not actually provisioned; the assessment status is not the same as key issuance (§7) — support ticket |

## 11. Product fact: the Unified.to connector and the QuickBooks surface split

**As of 2026-09-20, Unified.to's QuickBooks connector requests:** `openid`, `email`, `profile` and
`com.intuit.quickbooks.accounting` — the same four for every supported object, read and write alike, across
accounting, commerce, HRIS, payment, storage and task categories. It does **not** request
`com.intuit.quickbooks.payment`, `phone`, `address`, or any payroll scope. Scopes go on the authorize URL
space-delimited; the token exchange is a form POST with HTTP Basic client authentication, and refresh sends the
refresh token and grant type.

Other connector behaviour worth knowing before you register anything, all as of 2026-09-20:

- It carries **two host profiles on one app** — a Production profile on `https://quickbooks.api.intuit.com` and a
  Sandbox profile on `https://sandbox-quickbooks.api.intuit.com` — sharing the single authorize URL
  (`https://appcenter.intuit.com/connect/oauth2`) and the single token URL
  (`https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer`). That mirrors §4 exactly: the key pair, not the URL,
  picks the environment.
- It reads the **realm from the callback query string** when the connection is initialised and keeps it with the
  connection's stored credentials, then builds every request path from it. **One connection is one company**, and
  the connector treats it that way throughout — no subsidiary or multi-company fan-out.
- Identity comes from the OpenID userinfo endpoint, with the sandbox host substituted when the connection is
  pointed at the sandbox API.
- Reads pin **minor version 75** (with the enhanced-custom-fields include) where custom fields matter.
- Webhooks are **portal-configured**, matching §0 — there is no registration call.

**The QuickBooks surface is split across four separate connectors**, not one: `quickbooks` (this skill),
`quickbookspayment` (the Payments API — `com.intuit.quickbooks.payment`, a different API host, US-only),
`quickbooksdesktop`, and `quickbooksworkforce` (which authorizes against the QuickBooks Time / TSheets host with no
scopes at all). **This skill covers only the QuickBooks Online app.** If the user needs Payments, Desktop or
Workforce credentials, say so and stop — those are different registrations with different requirements, and
Payments in particular is an additional scope on the *same* app rather than a separate one.

**Confirm all of the above with the connector's owner before you register.** This is a snapshot of a codebase that
changes; it is a starting point for the conversation, not a specification.

## 12. Hand off — never commit the secret

- **Do not** write the client secret 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 the secret store or console.
- If a code change is needed (a redirect host, a scope, minor-version pinning, refresh-token persistence), keep it
  secret-free and say what the human must set out of band.
- Close with: app name and App ID; the workspace that owns it; **client ID per environment** and where each secret
  was delivered; authorize, token and revoke URLs; the API base URL per environment; the exact scope strings; the
  redirect URIs registered in each list; the assessment questionnaire's status and what remains; and anything left
  for the user to do.

## Stop and ask

Hand back to a human rather than guessing when: the assessment questionnaire asks about data handling, retention,
encryption, breach process, sub-processors, volumes or regulated industries; the questionnaire returns "Not
approved" (it may not be resubmittable — that is a support ticket, not a retry); the app would cross **500
connections** and therefore inherit the app-store security requirements; the client does not store rotated refresh
tokens or the `realmId` (report it — do not register and hope); someone proposes rotating a live client secret; the
request is really for Payments, Desktop or Workforce credentials (§11); app-store listing, legal terms or marketing
claims are in scope; or the portal does not match the **Platform state** section above.

## References

Verified 2026-09-20. Note that `developer.intuit.com` is a single-page app that returns HTTP 200 for any path, so a
status check does not prove a docs page exists — every URL below was reached through Intuit's own site search index
and its content read on that date. The `help.developer.intuit.com` links do return real 404s for bad paths and were
checked that way.

- Authorization and authentication overview — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization
- Set up OAuth 2.0 (authorize params, callback params, token exchange, expiry, revoke) — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
- OpenID Connect — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/openid-connect
- Set app redirect URIs — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/set-redirect-uri
- Authorization FAQ — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/faq
- OAuth 2.0 Playground — https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0-playground
- OIDC discovery (production) — https://developer.api.intuit.com/.well-known/openid_configuration/
- OIDC discovery (sandbox) — https://developer.api.intuit.com/.well-known/openid_sandbox_configuration/
- Change app settings — https://developer.intuit.com/app/developer/qbo/docs/get-started/app-settings
- Sandbox and tools — https://developer.intuit.com/app/developer/qbo/docs/develop/sandboxes
- Sandbox FAQ — https://developer.intuit.com/app/developer/qbo/docs/develop/sandboxes/sandbox-faqs
- Manage your sandboxes — https://developer.intuit.com/app/developer/qbo/docs/develop/sandboxes/manage-your-sandboxes
- Go live — https://developer.intuit.com/app/developer/qbo/docs/go-live
- Publish your app — https://developer.intuit.com/app/developer/qbo/docs/go-live/publish-app
- Publishing requirements and guidelines — https://developer.intuit.com/app/developer/qbo/docs/go-live/publish-app/platform-requirements
- Technical requirements for apps — https://developer.intuit.com/app/developer/qbo/docs/go-live/publish-app/technical-requirements
- App-store security requirements (incl. the 500-connection rule) — https://developer.intuit.com/app/developer/qbo/docs/list-on-the-app-store/security-requirements
- Maintaining compliance (annual review) — https://developer.intuit.com/app/developer/qbo/docs/list-on-the-app-store/maintaining-compliance
- App-store technical requirements (connect/disconnect behaviour) — https://developer.intuit.com/app/developer/qbo/docs/list-on-the-app-store/technical-requirements
- Minor versions of the API — https://developer.intuit.com/app/developer/qbo/docs/learn/explore-the-quickbooks-online-api/minor-versions
- Basic schema and data formats — https://developer.intuit.com/app/developer/qbo/docs/learn/rest-api-features
- Error codes — https://developer.intuit.com/app/developer/qbo/docs/develop/troubleshooting/error-codes
- Configure webhooks — https://developer.intuit.com/app/developer/qbo/docs/develop/webhooks/configure-webhooks
- Platform release notes — https://developer.intuit.com/app/developer/qbo/docs/release-notes/platform-release-notes
- QuickBooks Payments API — get started — https://developer.intuit.com/app/developer/qbpayments/docs/get-started
- QuickBooks Payments OAuth 2.0 — https://developer.intuit.com/app/developer/qbpayments/docs/develop/authentication-and-authorization/oauth-2.0
- Using the Online and Payments APIs together — https://developer.intuit.com/app/developer/qbo/docs/workflows/use-the-quickbooks-online-and-the-quickbooks-payments-apis-together
- App assessment and compliance FAQ — https://help.developer.intuit.com/s/article/New-app-assessment-process-FAQ
- Refresh token expiration and validity policy — https://help.developer.intuit.com/s/article/Validity-of-Refresh-Token
- Handling OAuth token expiration — https://help.developer.intuit.com/s/article/Handling-OAuth-token-expiration
- More than 25 redirect URIs — https://help.developer.intuit.com/s/article/More-than-25-redirect-URIs
- QuickBooks Online API best practices — https://help.developer.intuit.com/s/article/QuickBooks-Online-API-Best-Practices
- Developer portal (sign in / app dashboard) — https://developer.intuit.com/app/developer/homepage
- Workspaces — https://developer.intuit.com/workspaces
- Sandbox companies — https://developer.intuit.com/sandbox-companies
