Skip to content

Authorize a tool account

An enrolled OAuth profile lets you authorize a tool account without pasting a provider token into the application. The service exchanges the authorization code and stores the resulting credentials for authorized execution. Access tokens, refresh tokens and client secrets are never returned to the SDK.

Use the current human session in the selected organization and environment. Organization-owned connections require owner or admin authority. Personal connections remain bound to their user. The enrolled profile fixes the issuer, resource, permissions, redirect URI and client configuration; the callback cannot choose a different provider or destination.

List profiles through client.toolCredentialsOAuth.profiles(scope) in TypeScript or client.tool_credentials_oauth.profiles(scope) in Python. Supply the exact organization, environment, mode and reset generation.

Select the profile, connection owner and one to 32 sorted, unique permitted agent IDs. Choose an explicit authorization_expires_at in UTC and review that deadline. The service requires it to be in the future and within the selected profile’s max_authorization_seconds limit, between 60 and 2,592,000 seconds. Call begin(scope, request, profile, key) with a caller-chosen idempotency key. The selected profile summary is a separate argument so the SDK can check the returned authorization URL against its issuer origin, audience and permissions. It also checks the response’s scope, profile and exact authority-deadline pins, the authorization-code flow and the S256 PKCE parameters before returning the URL.

Show the selected provider and permissions, then navigate only after the person’s explicit action. The SDK does not open a browser. The authorization URL contains sensitive state: use it transiently and do not log or persist it. Do not send it to analytics or include it in a recovery record. Check expires_at before navigation: it is the separate ten-minute setup/review window, not the connection authority deadline.

Your application callback must match the redirect URI enrolled for the profile. Capture the callback’s code, state and issuer without altering them. Remove sensitive query parameters from the visible URL before application scripts, analytics or error reporting process the page. Use a no-referrer policy.

Call complete(scope, authorizationID, {state, issuer}, codeBytes) once. Python uses the same method name and accepts bytes. Code bytes are a separate transient argument, containing one to 4,096 printable, non-space ASCII bytes. HTTP uses {state, issuer, code_base64}; base64 does not provide encryption.

Complete does not take an idempotency key. The authorization ID controls the one-use exchange. A lost response is not permission to repeat that exchange. Clear callback inputs after submission, failure or navigation. Keep state, codes, authorization URLs and token responses out of persisted recovery data and logs. The SDK sanitizes failures rather than returning original sensitive transport errors or response details.

Use review(scope, authorizationID) to inspect the current result. The optional last argument is the original Begin request, which binds the returned profile, owner, agent allowlist and reconnect reference to that request.

StatusNext action
pendingAuthorization has not completed. Check its expiry before continuing.
exchangingInspect status again; do not resend the callback.
reviewShow the verified account, permissions, expiry and account digest for explicit review.
activatedRead the recorded credential version; do not treat another activation as a new connection.
rejectedThe authorization was refused. Start a new authorization after reviewing the cause.
uncertainInspect the recorded status and reconcile the uncertain exchange; do not repeat it automatically.
expiredThis authorization can no longer proceed. A previous account preview may remain as metadata.

oauth_exchange_uncertain is an HTTP 409 requiring inspection, not a retry action. oauth_authorization_rejected is an HTTP 403. Neither response tells the client to repeat the provider exchange.

After the person reviews the verified account and permitted agents, call activate(scope, review, key) with a new explicit idempotency key. Activation independently checks the reviewed account digest and current authority. It returns the normal credential connection and exact version reference. The connection version’s expires_at equals the reviewed authorization_expires_at. Shorter access-token expiry and serialized token refresh are private service state; refresh never extends that authority deadline. Read the connection’s current availability; refresh_required, refreshing and reconnect_required are not ready states even when record state remains active. Revocation and current-authority checks still apply. A successful provider authorization alone does not activate a credential, approve an action or authorize spending.

Before authorized execution, the service can renew an expired access token using the connection’s private refresh token. Concurrent callers share a serialized refresh attempt. They cannot start competing exchanges for the same generation. The resulting token must still represent the same verified account, exact permissions and current enrolled provider. Renewal never changes the connection owner, agent allowlist or reviewed authorization deadline.

A lost exchange or completion result does not make the previous refresh token safe to reuse. A refused or uncertain renewal requires reconnect rather than an automatic repeat of that provider exchange. While renewal is underway, availability is refreshing; read current connection metadata before deciding what to show. A successful earlier authorization does not guarantee that renewal or execution remains available.

For reconnect, Begin includes reconnect: {connection_id, expected} using the current connection’s exact reference. The profile, owner and permitted agents must match the existing connection. The service verifies the same account and activation creates the next immutable credential version.

No SDK method automatically retries. Recovery records may retain the original request, caller key, authorization ID and selected account/environment scope. They must omit the authorization URL, state, callback code and provider tokens. Keep records bound to the same signed-in account, mode and reset generation.

After an uncertain Begin result, an explicit recovery must use its original key and exact metadata. After an uncertain Complete result, inspect Review instead of resending the exchange. After uncertain activation, inspect the authorization and its credential reference; an explicit retry must retain the same activation key and reviewed account digest.

const api = client.toolCredentialsOAuth;
const profiles = await api.profiles(scope);
const profile = profiles.data[0];
const request = {
profile: profile.profile,
owner_kind: 'user' as const,
agents: selectedAgentIDs,
authorization_expires_at: reviewedAuthorityDeadline,
};
const start = await api.begin(scope, request, profile, beginKey);
// Use start.authorization_url only for the person's explicit navigation.
// After the callback, submit transient code bytes once:
const review = await api.complete(scope, start.id, callback, codeBytes, request);
// Inspect review.status and require explicit account approval before activation.
const connection = await api.activate(scope, review, activationKey);

All OAuth SDK methods use a human session and a 40-second request timeout. Profiles and credential services must be enabled for the environment. Synthetic contract fixtures test scope, redirect and callback boundaries; they do not establish live provider authorization or successful external tool execution.

Authorization deadlines use UTC timestamps with microsecond precision. Nonzero digits beyond six fractional decimal places are rejected rather than rounded. Trailing zero digits do not change the instant.

An owner can explicitly ensure current OAuth access before an authenticated MCP catalog probe or publication. Read the exact connection, review its current availability and submit its immutable reference:

const refreshed = await client.refreshToolCredentialOAuth(scope, connection.reference);

Python uses refresh_tool_credential_oauth(scope, connection_reference). The nested toolCredentialsOAuth.refresh / tool_credentials_oauth.refresh methods are equivalent. The request requires a fresh human session with locus:tools:write; API keys and workloads cannot perform this maintenance. It sends no tokens and needs no idempotency key. The server serializes token-generation exchange; it does not change the credential version or extend the reviewed authorization deadline.

The SDK never retries this request automatically. After a lost response, use toolCredentials.get / tool_credentials.get to inspect the exact connection. refreshing requires inspection; reconnect_required requires a newly reviewed authorization. Neither status authorizes repeating a provider exchange. A later explicit maintenance request can refresh a newly near-expiry generation, so this is not a permanently replayed operation receipt.