Skip to docs content
Browse documentation
On this page

OAuth: bring your own app

START HERE for third-party OAuth: register an OAuth app on the Integrations page, the exact redirect URI (https://app.minion.codes/oauth/callback), GitHub and Google presets, custom providers, token refresh, and how it differs from MCP OAuth.

Read this guide first for third-party OAuth (GitHub, Google, Linear, Spotify, and similar providers).

This guide covers the standard hosted OAuth path: you register an OAuth app with the provider, add it to Minion, and approve the connection once. Use it before building a package or package app that depends on the resulting integration.

For a walkthrough of Google OAuth (Gmail inbox reading), see Google OAuth walkthrough . Per-provider setup pages start at Connect a provider .

Default path: the Integrations page

Owners and admins manage OAuth at app.minion.codes/app/integrations . It has two cards:

  1. OAuth apps. Register the provider's app once: pick a Provider, paste the Client ID and Client secret, and optionally set default scopes and API hosts.
  2. Integrations. Pick an app and click Connect. The authorize, callback and token exchange run in your browser, and Minion stores the access and refresh tokens, encrypted, on the connection.

An agent can start the same flow with minion.integrations.connect({ provider }). It returns a connectUrl that a signed-in owner or admin opens to approve. Agents can't finish an OAuth login on their own.

This path does not require package-app-specific OAuth code.

Redirect URI

The redirect URI is:

https://app.minion.codes/oauth/callback

Register it in the provider console exactly as written, with no trailing slash. A self-hosted deployment uses its own origin plus /oauth/callback. The Integrations page shows the redirect URI for the origin you're on.

Provider setup checklist

The provider-side setup is the same for every provider:

  1. Create an OAuth app in the provider's developer console.
  2. Register the exact redirect URI above.
  3. Enable any APIs and scopes the integration needs.
  4. Paste the client ID and client secret into the OAuth apps card in Minion.

Presets and custom providers

Minion has built-in presets for GitHub and Google. A preset fills in the authorize and token URLs, token exchange style, PKCE, default scopes and API hosts, so you only paste a client ID and secret.

For any other OAuth 2 provider, choose Custom (any OAuth 2 provider) and fill in:

  • App name. Set it explicitly (for example slack). A custom app's name defaults to custom.
  • Authorization URL and Token URL. Look them up in the provider's official docs, and verify that both belong to the provider's own domain.
  • Revocation URL (optional). If set, disconnecting revokes the grant at the provider.
  • Client authentication. Secret in the request body (GitHub, Slack, Google style), HTTP Basic, or None for a public client that relies on PKCE.
  • Use PKCE (S256). On by default.
  • API hosts (required for custom). The only hosts the token may be sent to. An exact host, or *.example.com for subdomains. A wildcard does not cover the bare domain.
  • Default scopes. Separated by spaces or commas.

The token only ever goes to the app's API hosts. Calls to any other host are refused.

Provider pages for GitHub , Google , Slack , Discord , Notion , Figma , Salesforce and Spotify give the exact values.

Token refresh

All integrations refresh host-side. minion.integrations.fetch and createAuthenticatedFetch (from minion:runtime) attach the token, refresh it when it expires or the API answers 401, and retry. Raw tokens never enter the sandbox and are redacted from responses. There is no raw-token helper.

When a refresh fails because the grant was revoked, the integration status turns error and Minion emits integration.auth.failed with a reconnectUrl. A person fixes it by clicking Connect again. Successful connects and refreshes emit integration.auth.succeeded. Packages can subscribe to both (see package subscriptions ).

Scopes

The scopes you request are saved on the integration. To widen them, call minion.integrations.connect({ provider, scopes }) again with the extra scopes, or type them in Extra scopes on the Integrations page. Scopes add to what was requested before, and a person has to approve the wider grant. The token doesn't change until they do. On a first connect, scopes typed in Extra scopes replace the app's default scopes, so list every scope you need.

Some providers, such as GitHub Apps, return a token with no scopes because access comes from the App's permissions. Minion doesn't treat that as a missing scope.

Integration naming

An integration name is lowercase letters, digits, _ and -, starting with a letter. It defaults to the app's name. Use <provider>-<purpose> when you connect more than one account: google for the default, google-business for a second one. Call minion.integrations.list() up front when a provider may have several accounts connected.

To disconnect, use the Integrations page or minion.integrations.delete({ name }). Deleting revokes the grant at the provider when the app has a revocation URL.

Not the same as MCP OAuth

The Integrations page is for outbound provider OAuth. Minion's own MCP OAuth endpoints (/oauth/authorize and related routes) are for clients authenticating to Minion itself. Remote MCP servers that use OAuth are connected from the MCP servers page (see Connect remote MCP servers ).

When to use another guide

After a successful connect

A saved OAuth integration is auth credentials only. It is not an agent-callable package API.

Do not treat connect success as "the Google/GitHub/etc. package is ready." The next step is a smoke-test of auth, then a thin helpers package that owns the product API calls.

Agent checklist

  1. Confirm OAuth is the right auth shape.
  2. Tell the user the exact redirect URI to register: https://app.minion.codes/oauth/callback.
  3. If the OAuth app isn't registered yet, have an owner or admin add it on the Integrations page (client ID and secret go there, never in chat). Then call minion.integrations.connect and give the person the connectUrl.
  4. Wait for the person to approve, and check that minion.integrations.get({ name }) reports connected.
  5. Run the authenticated smoke test from the integration bootstrap guide. Do not persist access or refresh tokens with secrets.set.
  6. Build a helpers package only after the smoke test passes.

Package-first recommendation for OAuth integrations

For an OAuth integration with a successful connect and a passing smoke test:

  • treat the saved integration as credentials; put agent-facing calls in a helpers package
  • build a package app when the integration needs a hosted UI
  • keep provider API calls in package-owned backend code
  • keep reusable automation in package exports