Auth Providers API

An auth provider is one way to connect to a service, identified by its slug (google, google-sa, my-crm): how it signs in or which keys it takes. Its auth methods (specs) follow the OpenAPI security scheme kinds: oauth2 (authorization code or client credentials), api_key and http. The credential service resolves a slug by looking up a team row first, then a platform row.

Every auth provider belongs to a company, the organization behind the service (one catalog row with name, logo and legal links). Google's five slugs all carry provider: "google". The provider field on an auth provider row is the company slug; the row's own slug is what Credential.provider stores.

Renamed from auth schemes (INF-1079). The /auth-schemes… paths answer identically for one more release and are then removed; use /auth-providers…. In the SDKs AuthSchemeDTO is now AuthProviderDTO and AuthSchemeSpec is AuthMethod.

  • Platform auth providers — OAuth providers in the catalog (Google, Slack, GitHub, Linear, and 35 others). Every catalog provider is a platform row that the platform maintains from its built-in definitions. List them with GET /auth-providers?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-providers/{id} (workspace Admin → providers) and can still register additional platform slugs with POST /auth-providers and scope: platform when a service is not in the catalog.
  • Team auth providers — Your own OAuth app for a slug that is not already a platform row. Register with POST /auth-providers (default scope: team); client id and secret are written to the team vault. Teammates connect through POST /credentials like any other OAuth provider.

Auth providers 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-providers, GET /auth-providers/hooks, GET /auth-providers/{id}
credentials:writePOST /auth-providers, PUT /auth-providers/{id}, DELETE /auth-providers/{id}

Team auth providers: 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 auth providers: register, update, or delete with scope: platform requires a platform operator API key from the platform team (403 permission_denied otherwise). Listing platform rows (?scope=platform) only needs credentials:read.


List auth providers

GET /auth-providers

Team auth providers (default)

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

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

Platform auth providers

GET /auth-providers?scope=platform

Returns every platform auth provider 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. None of the thirty had a live platform OAuth app when they were written, so treat the first connect as the practical test.

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-providers?scope=platform" \2  -H "Authorization: Bearer inf_your_key"

Response (AuthProviderDTO[]):

FieldDescription
idAuth provider ID
slugCredential slug, used in /credentials/{slug}, Credential.provider, credentials: in inf.yml, and secret key prefixes
providerCompany slug the auth provider belongs to (google for google-sa). Defaults to slug
display_nameLabel shown in the vault and connect UI
icon_urlLogo URL shown on credential 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_worksCatalogue copy for the provider page
scopeteam or platform — who owns the row
connection_scopeDefault connection scope for new logins (team or user) when set on the row
specsAuth methods (see Auth method)
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 provider 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. Company catalog — If a company row exists for the auth provider'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 companies in the credential service.

Platform seeded rows — All 38 catalog slugs include a website in the built-in definitions (for example linear.app, zoom.us). When the platform writes these rows, it looks up a logo for each row that still has no icon_url (company icon first, then logo lookup by website). A logo already stored on a row is kept.

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

Team auth providers — Registering your own with website and no icon_url borrows a logo for the card only; it does not create a platform catalog entry. Updating website on PUT /auth-providers/{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.


List auth provider hooks

GET /auth-providers/hooks

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

Response (AuthProviderHooksDTO):

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)
auth_server_profilesAuthorization server profiles for outbound MCP OAuth (no auth provider row). Names registered in the API (today microsoft_entra for Microsoft Entra ID). The MCP connect flow picks one profile from discovered OAuth metadata (issuer host, then authorize endpoint, then token endpoint) and records it on the credential.
bash
1curl https://api.inference.sh/auth-providers/hooks \2  -H "Authorization: Bearer inf_your_key"

MCP resource auth providers

An MCP server that needs a credential signs in through an auth provider of its own, which the API creates when the server is registered or connected. The server's registration declares that slug in credentials, and calls to the server send the caller's credential for it.

Slug{name}-mcp. name is the server's slug (directory or workspace registration), else the distinctive label of its host (mcp.linear.app → linear-mcp). When another auth provider already holds the slug, the API numbers it: linear-2-mcp, linear-3-mcp, …
Scopeplatform when the public MCP directory lists the URL, so every workspace resolves the same slug. Otherwise team, so a private server's URL is never shown to another workspace.
OAuth serverskind: "oauth2" from discovery: resource is the resource the server's RFC 9728 metadata names when it names this server, else the server URL; issuer, pkce: true, callback: "mcp", and a profile when the authorization server needs one. A public client is registered for the auth provider (dynamic client registration or the Client ID Metadata Document) with client_auth: "none", unless the authorization server needs an OAuth app set up by hand.
API-key serverskind: "api_key" with one API_KEY field, sent as Authorization: Bearer <key>.
Companyprovider on the row is the catalog company for the server's slug or host domain (mcp.asana.com belongs to asana).

Get auth provider

GET /auth-providers/{id}

Returns one auth provider 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 provider

POST /auth-providers

Creates an auth provider. For team rows (default), client_id and client_secret are required and written to the team vault. For platform rows (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 slug (otherwise 409 conflict).

Body (AuthProviderCreateRequest):

FieldRequiredDescription
slugYesLowercase letters, digits, and single hyphens (for example my-crm), max 64 characters
display_nameYesHuman-readable name
specsYesAt least one auth method (see Auth method)
providerNoCompany slug, when the auth provider is one of several for one company. Defaults to slug
client_idTeam rowsOAuth client id — stored in the vault, not returned on later reads
client_secretTeam rowsOAuth client secret — stored in the vault
icon_url, website, description, docs_url, how_it_worksNoCatalogue and UI metadata; see Icons and logos
connection_scopeNoteam or user — default scope for credentials through this auth provider
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",7      "flow": "authorization_code",8      "authorization_endpoint": "https://crm.example.com/oauth/authorize",9      "token_endpoint": "https://crm.example.com/oauth/token",10      "scopes": ["read", "write"],11      "pkce": true,12      "client_auth": "basic"13    }14  ],15  "client_id": "your-client-id",16  "client_secret": "your-client-secret"17}

Set provider to the company slug when the auth provider is one of several for one company ("provider": "acme" on acme-mcp); it defaults to the slug.

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 auth provider's slug.


Update auth provider

PUT /auth-providers/{id}

Updates display fields, provider (moves the row under another company), connection_scope, and specs (AuthProviderUpdateRequest). 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 rows). Omit them to leave stored values unchanged. Platform updates require a platform operator; team updates require team admin.


Delete auth provider

DELETE /auth-providers/{id}

Removes the auth provider 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 method

Each entry in specs is an auth method (AuthMethod): one way to sign in to the slug. The kinds are the OpenAPI security scheme kinds; OAuth endpoints use the RFC 8414 names.

kindMeansFields
oauth2A sign-in or a server-to-server grantflow: authorization_code (default) or client_credentials; authorization_endpoint (authorization code only), token_endpoint, optional revocation_endpoint, and everything in the tables below
api_keyOne or more keys a person pastesfields (each key, label, placeholder, sensitive, optional); optional in (header or query), param_name, value_prefix (for example Bearer )
httpA pasted secret sent as an HTTP auth schemescheme: bearer or basic; optional bearer_format; fields

A client_credentials method has no redirect: POST /credentials exchanges the slug's client id and secret at the token endpoint and returns a connected credential at once (see Credentials API). An api_key or http method connects by saving its fields with POST /secrets and provider set to the slug, the same way catalogd API-key providers do.

Older names. Rows written before this change used kind: "oauth2_authorization_code", authorize_url and token_url. They are still accepted on read (and rewritten by a migration) but new requests must use the names below.

Validation (400 validation_error): at least one auth method; authorization_endpoint, token_endpoint, revocation_endpoint and userinfo_url must be https URLs; flow must be authorization_code or client_credentials; api_key and http need at least one field, with keys like ACME_API_KEY (uppercase letters, digits, underscores) and no duplicates; in needs param_name; hook names must be registered.

Authorize and token (oauth2)

FieldDescription
kindoauth2
flowauthorization_code (default) or client_credentials
authorization_endpointHTTPS authorization endpoint (authorization code)
token_endpointHTTPS token endpoint
revocation_endpointOptional HTTPS revocation endpoint, used on disconnect when revoke is not set
scopesDefault scopes requested on connect (connect may override)
required_scopesScopes always sent on connect and when adding scopes, even when the caller passes a narrower scopes list on POST /credentials. Use the auth provider's own scope names (same namespace as scopes). Typical values are the permissions the identity lookup (userinfo_url or a lookups entry) needs to resolve identifier and name, plus offline_access (or the service's refresh scope) when the defaults include it. Omit when identity comes from the token response or an endpoint any access token can call, or when the service sends no scope parameter (omit_scope_param). Built-in catalog providers set these on their platform row.
scope_aliasesMap friendly scope names to 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)
resourceRFC 8707 resource indicator: the protected resource the token is minted for (an MCP server's URL). Sent on the authorize, code, and refresh requests when the profile takes one
issuerThe authorization server's RFC 8414 issuer. A callback whose iss names another issuer is refused
authorization_response_iss_parameter_supportedWith issuer, the callback must carry an iss that names it (RFC 9207)
callbackName of the redirect URI the OAuth app was registered with (<app>/settings/secrets/oauth/<callback>). Empty means the slug. MCP resource auth providers use mcp
profileAn authorization server profile from auth_server_profiles in hooks, for servers that deviate from the specs. Empty means none
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
account_hint_paramAuthorize query parameter that names the connected account on add-scopes flows (Google and Microsoft use login_hint). The API sets it to the credential's account_identifier. Omitted on first connect and when the identifier is empty.
client_authbasic (default), body, or none — how client id/secret are sent to the token endpoint. none is a public client that sends no secret
token_requestform (default) or json — token request body encoding
headersExtra HTTP headers on requests to the service; 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 service 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.

When the auth method sets identifier or identity_hook, POST /credentials/oauth/complete must produce a non-empty account from the token exchange and lookups. Otherwise the connect fails and nothing is saved (the dashboard could not show whose account it is, and incremental auth could not target the same user). A lookup that returns 401 or 403, or 200 with no matching field, counts as the service refusing identity: fix scopes or the OAuth app and reconnect. A timeout, unreachable host, 408, 429, or 5xx is temporary when the lookup URL is the auth method's own (userinfo_url or a lookup, not a URL named by the token response): the login is saved without an account, and the account is learned when the login is next used.

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 services return extra query parameters on the redirect (QuickBooks realmId, Shopify shop, and so on). Auth method 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
capabilitiesCredential 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, an unknown kind or flow, or unknown hook names).

Declared from inf.yml

An app can carry an auth method inline under credentials.<slug>.auth in its inf.yml. It is validated at deploy with the same rules and seeds the auth provider when the slug is free. See Declaring credentials.


Credential configs

Auth providers appear in GET /credentials/configs (and GET /integrations/configs) like every other credential slug. When the row is one the team defined, CredentialConfigDTO includes auth_scheme_id (the auth provider's id; the field keeps its name) so the workspace UI can edit or remove it.

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

Connected accounts use the same CredentialDTO fields as other OAuth credentials. Use the auth provider's 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 credential 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.