Auth Schemes API

An auth scheme is the data that defines how OAuth works for a provider slug: authorize and token URLs, scopes, PKCE, identity templates, capabilities, and vault key layout. The credential service resolves a provider by looking up a team row first, then a platform row.

  • Platform schemes — OAuth providers in the catalog (Google, Slack, GitHub, Linear, and 35 others). Every catalog provider is a seeded platform row written when a platform operator runs POST /admin/auth-schemes/seed — the same primitive as the original eight built-ins. The API does not seed these rows on startup; run seed after a deploy that changes the built-in definitions. List them with GET /auth-schemes?scope=platform. Until a platform operator sets the shared OAuth app with PUT /credentials/{provider}/app, teams connect through their own OAuth app on the provider page, the same way Microsoft works today. Operators edit seeded rows with PUT /auth-schemes/{id} (workspace Admin → OAuth providers) and can still register additional platform slugs with POST /auth-schemes and scope: platform when a service is not in the catalog.
  • Team schemes — Your own OAuth app for a slug that is not already a platform row. Register with POST /auth-schemes (default scope: team); client id and secret are written to the team vault. Teammates connect through POST /credentials like any other OAuth provider.

Auth schemes store endpoints and OAuth settings in the database. The OAuth client id and secret live in the vault ({SLUG}_CLIENT_ID, {SLUG}_CLIENT_SECRET). After a user connects, access tokens are stored under {SLUG}_ACCESS_TOKEN (slug my-crm → env prefix MY_CRM).

→ Credentials API — connect, disconnect, and GET /credentials/configs
→ Secrets API — vault keys for client credentials and tokens


Scopes

Uses the same API key scopes as credentials:

ScopeEndpoints
credentials:readGET /auth-schemes, GET /auth-schemes/hooks, GET /auth-schemes/{id}
credentials:writePOST /auth-schemes, PUT /auth-schemes/{id}, DELETE /auth-schemes/{id}

Team schemes: create, update, and delete require the team admin role (or owner) in addition to credentials:write. List and get are available to any team member with credentials:read. Callers without the role receive 403 with code team_role_required.

Platform schemes: register, update, or delete with scope: platform requires a platform operator API key from the platform team (403 permission_denied otherwise). Listing platform schemes (?scope=platform) only needs credentials:read.


List auth schemes

GET /auth-schemes

Team schemes (default)

Returns every auth scheme registered by the API key's team, sorted by slug ascending.

bash
1curl https://api.inference.sh/auth-schemes \2  -H "Authorization: Bearer inf_your_key"

Platform schemes

GET /auth-schemes?scope=platform

Returns every platform auth scheme row — 38 seeded catalog providers plus any operator-defined platform rows. Sorted by slug. Any team member with credentials:read can call this; use it to inspect endpoints, default scopes, capabilities, and vault key names before connecting or declaring requirements in inf.yml.

The first eight slugs (google, slack, discord, x, microsoft, notion, salesforce, reddit) shipped as Go OAuth providers; the other thirty were added from each service's public OAuth documentation and are updated when an operator runs seed after deploy, like the originals. None of the thirty had a live platform OAuth app when they were written — treat the first connect as the practical test, and fix the row with PUT /auth-schemes/{id} (or re-run seed after correcting the built-in definition) if a path is wrong.

Seeded catalog slugs (38): airtable, asana, atlassian, bitbucket, box, calendly, canva, clickup, discord, dropbox, figma, github, gitlab, google, hubspot, intercom, klaviyo, linear, linkedin, mailchimp, microsoft, miro, monday, notion, pipedrive, quickbooks, reddit, salesforce, slack, spotify, stripe, tiktok, twitch, typeform, webflow, x, xero, zoom.

bash
1curl "https://api.inference.sh/auth-schemes?scope=platform" \2  -H "Authorization: Bearer inf_your_key"

Response (AuthSchemeDTO[]):

FieldDescription
idAuth scheme ID
slugProvider key — used in /credentials/{slug}, Credential.provider, and secret key prefixes
display_nameLabel shown in the vault and connect UI
icon_urlProvider logo URL shown on integration cards and the provider page (see Icons and logos)
websiteService home domain or URL (for example github.com) — used to resolve icon_url when it is empty
description, docs_url, how_it_worksCatalog copy for the provider page
scopeteam or platform — who owns the scheme row
connection_scopeDefault connection scope for new logins (team or user) when set on the scheme
specsAuthentication specs (see Auth scheme spec)
client_id_keyVault secret name for the OAuth client id (for example SLACK_CLIENT_ID)
client_secret_keyVault secret name for the OAuth client secret

Icons and logos

Each auth scheme row can carry an icon_url for the workspace and API catalog UI. You can set it explicitly on create or update; when you omit it but set website, the API resolves a logo for you.

Resolution order (best effort — unknown brands or lookup failures leave icon_url empty):

  1. Providers catalog — If a catalog row exists for the scheme's slug and has an icon, that URL is used.
  2. Logo lookup by website — Otherwise the API fetches a logo for the domain in website (accepts acme.com or a full https:// URL; www. is stripped). The same lookup path is used for integration providers in the credential service.

Platform seeded schemes — All 38 catalog slugs include a website in the built-in definitions (for example linear.app, zoom.us). When an operator runs POST /admin/auth-schemes/seed, the API looks up a logo synchronously for each row that still has no icon_url (catalog icon first, then logo lookup by website). The response lists any slugs that stayed unbranded and why. Re-running seed keeps logos already stored on a row unless you clear icon_url. Once a logo is stored, later seeds keep it unless you set icon_url explicitly.

Platform operator rows — Registering or updating a platform scheme (scope: platform) with website and no icon_url runs the same lookup synchronously on write. When the slug is not yet in the providers catalog, a catalog row is created (OAuth type) so other surfaces can reuse the name and logo.

Team schemes — Registering your own scheme with website and no icon_url borrows a logo for the scheme card only; it does not create a platform catalog entry. Updating website on PUT /auth-schemes/{id} re-runs lookup when the request leaves icon_url empty.

To use a fixed asset, set icon_url on create or update; the API does not overwrite a non-empty stored icon when you change other fields.


Seed platform catalog (operators)

POST /admin/auth-schemes/seed

Writes the API's built-in OAuth provider definitions as platform auth scheme rows and resolves logos for rows without one. Requires platform admin credentials (same /admin router as other operator endpoints).

Run this after a deploy that changes built-in provider definitions — startup no longer seeds auth schemes. In the workspace, Admin → OAuth providers (/admin/auth-schemes) exposes the same operation as Seed built-ins (toast summarizes created, updated, branded, and any slugs still without a logo). The call is idempotent for known slugs: existing platform rows with the same slug are updated from the built-in definition (operator-added platform slugs that are not in the built-in list are left alone). Logos found on a previous seed or set by an operator are preserved when the built-in row leaves icon_url empty.

Response (AuthSchemeSeedResult):

FieldDescription
createdNew platform rows inserted
updatedExisting built-in slugs refreshed
brandedRows that received an icon_url during this call
unbrandedMap of slug → reason when a row still has no logo (for example no website to look a logo up by, logo service not configured, no logo found for example.com)
bash
1curl -X POST https://api.inference.sh/admin/auth-schemes/seed \2  -H "Authorization: Bearer $ADMIN_API_KEY"

Example response:

json
1{2  "data": {3    "created": 0,4    "updated": 38,5    "branded": 2,6    "unbranded": {7      "custom-saas": "no logo found for custom-saas.example"8    }9  },10  "messages": []11}

List auth scheme hooks

GET /auth-schemes/hooks

Returns the hook names a scheme spec may reference when declarative fields are not enough. Hooks are implemented in the API; custom team schemes should only use names from this list (unknown names fail validation on create/update).

Response (AuthSchemeHooksDTO):

FieldDescription
token_responseNon-standard token response parsers (for example slack_v2)
identityDerive account id and display name when templates are insufficient (for example discord)
varsSubstitute URL variables from extra app keys (for example microsoft_tenant, salesforce_host)
bash
1curl https://api.inference.sh/auth-schemes/hooks \2  -H "Authorization: Bearer inf_your_key"

Get auth scheme

GET /auth-schemes/{id}

Returns one scheme owned by the team. Platform rows respond 403 permission_denied for non-operators. Responds 404 when the id is missing or belongs to another team.


Register auth scheme

POST /auth-schemes

Creates a scheme. For team schemes (default), client_id and client_secret are required and written to the team vault. For platform schemes (scope: platform), omit client credentials — set the OAuth app later with PUT /credentials/{provider}/app.

The slug must be unique and must not collide with a built-in or catalog provider slug (otherwise 409 conflict).

Body (AuthSchemeCreateRequest):

FieldRequiredDescription
slugYesLowercase letters, digits, and single hyphens (for example my-crm), max 64 characters
display_nameYesHuman-readable name
specsYesAt least one spec (see Auth scheme spec)
client_idTeam schemesOAuth client id — stored in the vault, not returned on later reads
client_secretTeam schemesOAuth client secret — stored in the vault
icon_url, website, description, docs_url, how_it_worksNoCatalog and UI metadata; see Icons and logos
connection_scopeNoteam or user — default scope for connections through this scheme
scopeNoteam (default) or platform (platform operators only)

Example (team authorization code + PKCE):

json
1{2  "slug": "my-crm",3  "display_name": "My CRM",4  "specs": [5    {6      "kind": "oauth2_authorization_code",7      "authorize_url": "https://crm.example.com/oauth/authorize",8      "token_url": "https://crm.example.com/oauth/token",9      "scopes": ["read", "write"],10      "pkce": true,11      "client_auth": "basic"12    }13  ],14  "client_id": "your-client-id",15  "client_secret": "your-client-secret"16}

Register this redirect URI in the third-party OAuth app before connecting:

code
1https://app.inference.sh/settings/secrets/oauth/{slug}

Use your staging or local app origin when testing outside production (same path pattern as Slack BYOK).

After registration, start OAuth with POST /credentials (or POST /integrations) using provider set to the scheme's slug.


Update auth scheme

PUT /auth-schemes/{id}

Updates display fields, connection_scope, and specs. The slug cannot change — it is the stable provider key on credentials and secrets.

Optional body fields include icon_url, website, description, docs_url, how_it_works, and connection_scope. Setting website without icon_url triggers the same logo resolution as on create (see Icons and logos).

Optional client_id / client_secret rotate vault secrets when provided (team schemes). Omit them to leave stored values unchanged. Platform scheme updates require a platform operator; team scheme updates require team admin.


Delete auth scheme

DELETE /auth-schemes/{id}

Removes the scheme and deletes its OAuth app secrets from the vault. Responds 400 validation_error when a credential for this slug is still connected — disconnect first with DELETE /credentials/{provider}.

Response:

json
1{ "data": { "deleted": true }, "messages": [] }

Auth scheme spec

Today only oauth2_authorization_code is supported. Each spec describes one OAuth flow for the provider slug.

Authorize and token

FieldDescription
kindoauth2_authorization_code
authorize_urlHTTPS authorization endpoint
token_urlHTTPS token endpoint
scopesDefault scopes requested on connect (connect may override)
scope_aliasesMap friendly scope names to provider wire values (credentials store the alias)
scope_paramQuery parameter name for scopes (default scope; Slack uses user_scope)
scope_separatorSeparator between scopes (default space; Slack uses comma)
omit_scope_paramOmit the scope parameter entirely (Notion)
pkceUse PKCE on the authorize request (recommended)
client_id_paramQuery/body field name for the OAuth client id (default client_id; TikTok uses client_key)
extra_authorize_paramsQuery parameters appended to the authorize URL (for example Google access_type=offline)
add_scopes_paramsParameters used when re-authorizing for more scopes
client_authbasic (default) or body — how client id/secret are sent to the token endpoint
token_requestform (default) or json — token request body encoding
headersExtra HTTP headers on provider requests; values are templates (for example Twitch Client-Id: {{client_id}})

Identity and disconnect

FieldDescription
userinfo_urlHTTPS userinfo endpoint fetched with the access token (shorthand for a lookup named user)
userinfo_url_pathJSON path inside the token response to a userinfo URL (Salesforce)
lookupsAdditional HTTP calls after the token exchange, each with name, url, optional method (GET default or POST), body, content_type, and headers. The decoded response becomes a document root named after name (for example user, resources). Lookups run after userinfo, in order.
identifierTemplate for the connected account id (for example {{user.email}})
nameTemplate for display name
metadataKey → template map stored on the credential
token_response_hookNamed hook when the token response is not RFC 6749 shape
identity_hookNamed hook when identifier/name cannot be templated
vars_hookNamed hook to build URL variables from extra app keys
refreshalways refreshes on every read when the provider hides expiry; default refreshes near expiry
revokeOptional revoke endpoint on disconnect (url, style: bearer, query, or form)

Template placeholders

URLs, headers, lookup bodies, identifier, name, metadata, and env values are strings with {{path}} placeholders (double braces). That lets JSON or GraphQL bodies contain literal { / } without being parsed as templates.

The document grows as the OAuth flow runs:

RootWhen available
client_idAfter authorize (and on token/lookup requests)
vars.* and each bare var nameFrom the vars hook (for example {{tenant}} in Microsoft URLs)
access_token, token.*After the token exchange (token.* is the decoded token response)
callback.*Query parameters from the redirect back to inference.sh (for example QuickBooks realmId, Shopify shop) — forwarded by the app callback page into CompleteOAuth
<lookup>.*Each lookup response by name; user.* is the userinfo lookup
expires_at, metadata.*, app.*Env only: token expiry (RFC 3339), stored metadata, extra OAuth app keys

Array elements use numeric indexes: {{resources.0.id}} (Atlassian cloud id, Xero tenant). {{a|b}} picks the first path that resolves. Lookup URLs and headers may include {{access_token}} and {{client_id}} (HubSpot token-in-URL, Twitch Client-Id).

If every placeholder in a template is empty, env/metadata entries are omitted and identifier/name render blank.

Examples:

  • Linear — POST GraphQL lookup with a JSON body; identity from nested fields: identifier: "{{user.data.viewer.email}}".
  • Dropbox — POST lookup with body null to the account endpoint.
  • QuickBooks — metadata.realm_id: "{{callback.realmId}}" with no extra userinfo call.
  • Mailchimp — lookup headers override the default bearer (OAuth Authorization header).

Callback query parameters on OAuth complete

Some providers return extra query parameters on the redirect (QuickBooks realmId, Shopify shop, and so on). Auth scheme templates read them as {{callback.*}} after OAuth completes.

When you call POST /credentials/oauth/complete (alias POST /integrations/oauth/complete), pass those parameters in optional params: a string map of every callback query key except code and state, which are top-level fields on the request body. The workspace OAuth callback page forwards redirect query params this way automatically.

json
1{2  "provider": "quickbooks",3  "type": "oauth",4  "code": "authorization_code",5  "state": "state_abc123",6  "params": {7    "realmId": "1234567890"8  }9}

Omit params when the redirect carries only code and state. In @inferencesh/sdk, the generated type is CredentialCompleteOAuthRequest with optional params?: Record<string, string>.

Capabilities, app keys, and runtime env

FieldDescription
capabilitiesIntegration capabilities (key, scopes, satisfies, display_name, description) — used by GET /credentials/configs and requirement checks
app_keysExtra OAuth app fields beyond client id/secret (labels and vault keys)
envEnvironment variables injected on run (defaults to {PREFIX}_ACCESS_TOKEN and expiry)

Validation errors use 400 with code validation_error and a descriptive detail (for example non-HTTPS URLs, unsupported kind, or unknown hook names).


Integration configs

Auth scheme providers appear in GET /credentials/configs (and GET /integrations/configs) like other OAuth integrations. When the row comes from an auth scheme, CredentialConfigDTO includes auth_scheme_id so the workspace UI can edit or remove team-defined schemes.

Provider resolution for connect and runtime uses the registry (non-OAuth providers such as google-sa, gcp, and MCP) then team auth scheme → platform auth scheme (platform rows are cached briefly on each API instance).

Connected accounts use the same CredentialDTO fields as other OAuth integrations. Use the scheme slug as provider in apps, agents, and POST /credentials/check.


For slug my-crm:

Secret keyPurpose
MY_CRM_CLIENT_IDOAuth client id (written at registration)
MY_CRM_CLIENT_SECRETOAuth client secret
MY_CRM_ACCESS_TOKENAccess token after a user connects (managed by the platform)

Apps and agents read the access token from the environment using the {PREFIX}_ACCESS_TOKEN name when the integration is declared in inf.yml.

we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.