← library
Review-gated OAuth
Customer service / messaging platform·agent-generated · deepseek-v4.1-flash·checked 2026-10-28
difficulty
3/5

Third-party OAuth exists and is self-serve for your own workspace, but any app reaching other customers' Intercom data — even unlisted — must pass Intercom review (7–14 days per docs, up to 7 business days per the console) with a recorded install/uninstall video and scope-by-scope justification.

First working connection
~4–8 hours
Production / approved
~2–4 weeks (7–14 day review, plus a resubmission buffer)
Composio managed OAuth
Available: ship today
Composio lists a managed OAuth app for this toolkit — you can ship on it while your own app is set up or reviewed.
who does itagent 4you 5provider 0
evidencetested 4sourced 29inferred 14
◆ agent run10 pages read25/35 quotes verified verbatim39s$0.020deepseek-v4.1-flash

01Pick your path

Private app on your own workspace
when The agent only acts on your own team's Intercom data.
→ Access token against your own paid workspace; no OAuth flow, no review, usable in minutes. Cannot be installed by other companies.
Public unlisted app (OAuth)
when Your agent acts on behalf of other Intercom customers and you drive installs from your own site.
→ OAuth required; full review still required; approved as public-unlisted, installable only from your product. No Installation status section needed.
Public listed app in the App Store
when You want App Store discovery and co-marketing.
→ Review plus full listing package (512px PNG icon, 2–5 images ≥1600×1000, 70-char short description, privacy policy, ToS, pricing model) and App Partner Program form; auto-enrolment in the partner programme.

02Time & effort

Hands-on work
~1 workdays – ~3 workdays
Waiting on others
7 days – 2 weeks
You're actually working
7% of elapsed time
Phases
6
start↑ first working connection, then production pathproduction
  1. DEVCreate dev workspace, app, enable OAuth20–40 minutessourced[S3][S9]Free development workspace; tick 'Use OAuth' on the Authentication page; add HTTPS redirect URLs; pick scopes on the Authorization page.
  2. DEVBuild authorize → callback → token exchange1–3 hourssourced[S9]Agent writes the redirect builder with region routing and state, the callback handler, and the /me verification call.
  3. DEVTest install/uninstall in dev workspace30–90 minutesinferred[S4][S8]Duration is my estimate; the docs only say to keep a separate dev app and test there.
  4. PRODWrite listing copy, record end-to-end video, submit1–4 hourssourced[S8][S10]Video must show install with OAuth (full client_id + state), the whole feature, and uninstall.
  5. PRODIntercom review + possible resubmissionwait 7–14 dayssourced[S4][S8]Docs say 7–14 days; console banner says up to 7 business days; feedback arrives as an in-app Messenger conversation.
  6. PRODGo live unlisted, then optionally list1–2 daysinferred[S8]Approval makes the app public-unlisted; flipping 'List in App store' is a separate choice and later edits need re-approval.
agent can do it you waiting on providerbar widths ∝ √time, so short tasks stay visible

03Steps

0/9 done

Get a working connection

  1. Create a development workspace and a dev appyouinferred[S3][S8]

    Sign up for a free development workspace, create an app, and create a second app for production so the two never share URLs or scopes.

    ↗ developers.intercom.com/
    ◆ prepared by agent · Intercom Developer Hub (app.intercom.com)
    https://app.intercom.com/
  2. Enable OAuth and register HTTPS redirect URLsyouinferred[S9]

    On the Authentication page tick 'Use OAuth'; under Redirect URLs add HTTPS callbacks (test + prod allowed, first is default). Private-app tokens are the alternative only for your own workspace.

    ↗ developers.intercom.com/docs/build-an-integration/learn-more/authentication/setting-up-oauth.md
  3. Choose the narrowest scopes and justify eachagentsourced ❝[S7][S4][S8]

    Pick only scopes that map to shipped features; document the mapping — over-privileged scopes are the top rejection cause and the reviewer checks the video against the scopes.

    “Over-privileged scopes are the most common reason that apps are initially rejected in the approval process.” — [S7] verified verbatim
    ◆ prepared by agent · Scope justification draft
    Feature → scope map (draft for review)
    - Contact lookup card → Read one user and one company
    - Ticket sync → Read tickets, Write tickets
    - Agent replies/closes → Write conversations
    Not requested: Write users and companies, Delete conversations and metrics, Read admin activity logs.
    Each scope above maps to a shipped screen in the app; none are speculative.
  4. Redirect to the regional authorize host with stateagentsourced ❝[S9]

    Redirect the user to the regional /oauth endpoint with client_id and a random state; store state server-side to block CSRF. Match the host to the workspace region — Google sign-in fails on the wrong host.

    “One exception: if the customer signs in with Google, the wrong regional host fails rather than prompting, so matching the region is required in that case.” — [S9] verified verbatim
    ◆ prepared by agent · Authorize URL builder (region-aware)
    https://app.intercom.com/oauth?client_id=$CLIENT_ID&state=$STATE      # US
    https://app.eu.intercom.com/oauth?client_id=$CLIENT_ID&state=$STATE    # EU
    https://app.au.intercom.com/oauth?client_id=$CLIENT_ID&state=$STATE    # AU
  5. Exchange the code for an access token server-sideagentsourced ❝[S9]

    Handle the callback (?code=&state=), verify state, then POST code + client_id + client_secret to https://api.intercom.io/auth/eagle/token. Keep the trailing '=' in the code or you get 401 'Unauthorized Code'.

    “The code value in the redirect URL may contain a = at the end which needs to be included.” — [S9] verified verbatim
    ◆ prepared by agent · Server-side token exchange
    curl -X POST https://api.intercom.io/auth/eagle/token \
      -d code="$CODE" -d client_id="$CLIENT_ID" -d client_secret="$CLIENT_SECRET"
    
    # -> {"token_type":"Bearer","token":"...","access_token":"..."}
  6. Verify the token and wire rate-limit handlingagentinferred[S9][S6]

    Verify the token with GET https://api.intercom.io/me (Bearer auth, Intercom-Version header), then add 429/backoff handling using X-RateLimit-Remaining and X-RateLimit-Reset.

    ↗ developers.intercom.com/docs/references/1.0/rest-api/errors/rate-limiting.md

Make it production-ready

  1. Record the end-to-end video and fill the listingyousourced ❝[S8]

    Record installation with OAuth working (show the full client_id and state in the URL), the end-to-end feature, and uninstallation. Missing any of the three means re-recording.

    “Please show the full client_id and state parameter in the OAuth URL.” — [S8] verified verbatim
    ↗ developers.intercom.com/docs/publish-to-the-app-store/listing-your-app.md
  2. Submit for review and answer feedback in the Messengeryousourced ❝[S8]

    Submit from the Test and publish section; the app stays private during review, feedback arrives as a Messenger conversation, and requested scope changes mean resubmission.

    “Our team will reach back out to you in an in-app conversation in the Messenger (find it at the bottom right of any Intercom pages you visit) to let you know about any changes that need to be made to your app.” — [S8] verified verbatim
    ↗ developers.intercom.com/docs/publish-to-the-app-store/review-publish-your-app.md
  3. Go live unlisted first, list in the App Store only when readyyousourced ❝[S8][S10]

    Approval yields a public-unlisted app you can beta-test; choosing 'List in App store' later is a separate step, and any post-approval edit must be re-approved.

    “When your app is approved, it will become a public unlisted app.” — [S8] verified verbatim

04Requirements, scopes & nuances

You'll need

  • ▸A Developer Hub app with client_id and client_secret (Basic Information page). sourced ❝[S9]
  • ▸At least one HTTPS redirect URL registered under Redirect URLs; multiple allowed, first is default. sourced ❝[S9]
  • ▸A server-side component that can hold the client_secret and perform the token POST (confidential client). sourced ❝[S9]
  • ▸Published privacy policy and end-user agreement, GDPR-capable data handling, and Intercom audit permission if asked. inferred[S5][S10]
  • ▸For public apps: app description, test account credentials if your product is paid, and an end-to-end video showing OAuth install + uninstall. inferred[S8]
  • ▸Separate development and production apps in the Developer Hub (name the dev one [Dev]/[Staging]). sourced ❝[S4][S8]

Gotchas

  • !Do not point your client at app.intercom.com/.well-known/openid-configuration — the 200 response is Intercom's own Okta staff SSO (issuer intercom.okta.com/oauth2/default, scopes okta.myAccount.*). There is no self-serve client registration there; a fake client_id gets a 400. inferred
  • !Intercom's own OAuth has no published discovery document: api.intercom.io/.well-known/openid-configuration is 404 and /.well-known/oauth-authorization-server is 406. Hardcode authorize, token and /me endpoints. inferred
  • !There are three authorization hosts (US app.intercom.com, EU app.eu.intercom.com, AU app.au.intercom.com); the wrong host is a wasted step for most users and a hard failure for Google sign-in. inferred[S9]
  • !Token exchange is a confidential-client POST with client_id + client_secret to api.intercom.io/auth/eagle/token — so the flow must terminate on a server you control, never in the browser or in the agent runtime. inferred[S9]
  • !If you use Canvas Kit (Messenger/Inbox apps), four scopes are auto-selected and cannot be deselected — 'Read and list users and companies', 'Read conversations', 'Read admins', 'Gather App data'. On the review form those must be justified like any other. sourced ❝[S7][S9]
  • !Webhook topics are tied to scopes: a user-created webhook needs 'Read and write users', so webhook plans drive scope requests (and rejections). sourced ❝[S7]
  • !Rate limits are 10,000 calls/min per app and 25,000/min per workspace, spread across 10-second windows (≈1,666 per 10s); over-limit returns 429 with X-RateLimit-* headers. sourced ❝[S6]
  • !The two review-time claims disagree: the Developer Hub banner says up to 7 business days, 'Building Successful Apps' says 7–14 days. Plan for 2 weeks plus one resubmission. inferred[S4][S8]
Scope / permissionTierNoteEvidence
Read one user and one companybasicNarrowest contact lookup — prefer over 'Read and list users and companies' for a single-contact card.sourced ❝[S7]
Read tags / Read counts / Read status of jobsbasicMetadata-only reads; safe defaults.sourced ❝[S7]
Read conversationssensitiveExposes customer message content — expect scrutiny in review.sourced ❝[S7]
Write conversationssensitiveAgent replies to and closes real customer conversations — the highest-risk write for an AI agent.sourced ❝[S7]
Read tickets / Write ticketssensitiveWrite tickets is create-only per the table; still requires ticket scope justification in the video.sourced ❝[S7]
Delete conversations and metricsrestrictedIrreversible; almost never justifiable for an assistant agent.sourced ❝[S7]
Read admin activity logsrestrictedTeammate surveillance data — pairs badly with an agent's purpose.sourced ❝[S7]
Canvas Kit default set (Read and list users and companies, Read conversations, Read admins, Gather App data)gatedAuto-selected and cannot be deselected if the app uses Canvas Kit — you inherit four broad scopes whether or not your feature needs them.sourced ❝[S7][S9]

05If you get blocked

✕Rejection for over-privileged scopes — the single most common initial rejection.sourced ❝[S7][S8]

→ Ship the minimal feature→scope map; trim anything without a shipped feature, then resubmit on the Review changes tab.

✕Redirect URLs must be HTTPS, so a local http://localhost callback cannot be registered.sourced ❝[S9]

→ Use a tunnel (ngrok/Cloudflare Tunnel) or a staging host with a real certificate; register the URL in the Redirect URLs section.

✕No privacy policy / end-user agreement → cannot pass app review or listing.sourced ❝[S5][S10]

→ Host a privacy policy and end-user agreement before submitting; they are mandatory listing fields and platform requirements.

✕Customer signed in with Google on the wrong regional authorization host — the flow fails instead of prompting for region.inferred[S9]

→ Detect region (or ask) and route EU→app.eu.intercom.com, AU→app.au.intercom.com; do not default everyone to the US host.

✕Token exchange returns 401 'Unauthorized Code'.sourced ❝[S9]

→ Pass the code verbatim, including the trailing '=', then retry the token POST.

✕Video lacks the full client_id/state or the uninstall step → automatic resubmission.inferred[S8]

→ Re-record showing the full client_id and state in the OAuth URL, the end-to-end feature, and uninstall.

06Evidence: what was tested, sourced, and still uncertain

$ probes: live GETs with a dummy client_id, no credentials
✓ Discovery document (/.well-known/openid-configuration) → 404
https://api.intercom.io/.well-known/openid-configuration
not published
∴ Intercom's public API does not publish OAuth discovery metadata — your client cannot auto-configure, and any library that insists on a discovery URL needs manual endpoints.
✓ Discovery document (/.well-known/oauth-authorization-server) → 406
https://api.intercom.io/.well-known/oauth-authorization-server
not published
∴ Second confirmation there is no RFC 8414 metadata on the API host (406, not JSON). Hardcode https://app.intercom.com/oauth and https://api.intercom.io/auth/eagle/token.
✓ Discovery document (/.well-known/openid-configuration) → 200
https://app.intercom.com/.well-known/openid-configuration
issuer=https://intercom.okta.com/oauth2/default; authorization_endpoint=https://intercom.okta.com/oauth2/default/v1/authorize; token_endpoint=https://intercom.okta.com/oauth2/default/v1/token; scopes_supported=okta.myAccount.appAuthenticator.maintenance.manage okta.myAccount.appAuthenticator.maintenance.read okta.myAccount.appAuthenticator.manage okta.myAccount.appAuthenticator.read okta.myAccount.authenticators.manage okta.myAccount.authenticators.read okta.myAccount.email.manage okta.myAccount.email.read okta.myAccount.manage okta.myAccount.oktaApplications.read okta.myAccount.organization.read okta.myAccount.phone.manage
∴ A 200 here is misleading: the issuer is intercom.okta.com/oauth2/default and every advertised scope is okta.myAccount.* — Intercom's internal staff identity, not a third-party API authorization server.
✓ Authorize endpoint with a fake client_id → 400
https://intercom.okta.com/oauth2/default/v1/authorize?client_id=probe_invalid_client&response_type=code&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
text/html;charset=utf-8 — body mentions "errors-v2.css"
∴ The Okta tenant exists but rejects unknown client_ids with a 400 HTML error page; there is no self-serve client registration endpoint for your app there.

Sources read

  1. [S1] Authentication | Intercom and Fin Developer Platform official
  2. [S2] Setting up OAuth | Intercom and Fin Developer Platform official
  3. [S3] Intercom Developers | Intercom and Fin Developer Platform official
  4. [S4] Building Successful Apps official
  5. [S5] Intercom Platform Guidelines official
  6. [S6] Rate Limiting official
  7. [S7] OAuth Scopes official
  8. [S8] App Review and Publishing official
  9. [S9] Setting up OAuth official
  10. [S10] Listing your App official
  11. [composio] Composio — Managed OAuth apps directory official
  12. [probe] Live HTTP probes run by the agent (dummy client_id, no credentials) probe

Still uncertain

  • ? The fetched pages never state the access token's lifetime or whether a refresh token exists — the response shows only token_type/token/access_token. Assume re-authorization is required when a token dies; confirm before designing long-lived agent sessions.
  • ? No PKCE, device flow, or dynamic client registration is documented; I infer it is a classic confidential-client authorization-code flow only. If your agent has no backend, that is a design blocker, not a documentation gap I can disprove.
  • ? Sources [S1] and [S2] returned 404 — Intercom's docs moved; the live guidance is [S9] (setting-up-oauth.md). Old URLs may be in your bookmarks.
  • ? The scope tier split in this guide (basic/sensitive/restricted/gated) is my judgement; Intercom publishes only a flat scope table plus the Canvas Kit forced set.
  • ? Review duration conflicts between sources (up to 7 business days vs 7–14 days); neither states a rejection-resubmission turnaround.
  • ? Whether App Partner Program details are required for unlisted apps is not stated — [S10] ties it to listing/appearing in the store, [S8] lists it as a sidebar form.
  • ? Generated automatically; no app was registered and no review was submitted.