Choose a credential
API keys and OAuth access tokens are credentials for a Shodai account, not separate resource containers. Credentials for the same account use that account’s entitlements and can access agreements created by the account and webhook subscriptions owned by it, subject to credential scopes and resource-level access rules. Revoking one credential does not delete the account’s agreements or webhook subscriptions.
API keys
UseX-API-Key as the canonical header. Authorization: Bearer cns_pk_... is also supported for clients that cannot set a custom API-key header; it is an API-key compatibility representation, not an OAuth access token.
Delegated OAuth
Delegated OAuth lets your application act for a Shodai user after that user signs in and approves the requested access. Shodai supports public clients with no client secret, the authorization-code flow, mandatory S256 PKCE, short-lived access tokens, and rotating refresh tokens. Send an access token as:- Register a public application with its redirect URIs and allowed scopes.
- Send the user to Shodai sign-in and consent with S256 PKCE.
- Let the user review the application, callback destination, and requested permissions, then approve or deny access.
- Exchange the returned authorization code for an access token and refresh token.
- Refresh and persist the rotated session until the application or user disconnects it.
Register the application
The normal setup path is OAuth apps in the Developer Portal. A registered public client ID begins withcns_oa_... and has no client secret. Register every callback URI and allow only the scopes the application needs.
Redirect URIs must match exactly. An HTTP loopback callback using 127.0.0.1, [::1], or localhost may use a different port when its hostname and path match and the callback has no query string.
An HTTPS Client ID Metadata Document is the advanced alternative to portal registration. The document URL itself is the client_id:
client_id must exactly match its HTTPS URL. Non-loopback redirect URIs must use HTTPS and share the document URL’s origin; HTTP loopback callbacks remain supported.
Discover endpoints
Fetch<issuer>/.well-known/oauth-authorization-server and use the returned authorization_endpoint, token_endpoint, and revocation_endpoint. Do not derive or hard-code those endpoint paths.
Authorize the user
Send these parameters to the discovered authorization endpoint:client_id: the registered client ID or metadata document URLredirect_uri: a matching registered callbackresponse_type=codestate: a random value your application validates on returncode_challenge: the S256 challenge derived from the PKCE verifiercode_challenge_method=S256scope: the smallest space-delimited set of permissions the application needs
code and the original state to the callback. Denial returns error=access_denied and the original state. Validate state before accepting either callback.
Exchange an approved code at the discovered token endpoint with a form-encoded request containing grant_type=authorization_code, client_id, code, the identical redirect_uri, and the original code_verifier.
The response includes access_token, expires_in, scope, and a rotating refresh_token. Store both tokens using storage appropriate for your application; do not expose them in browser URLs or logs.
Refresh the session
Before the access token expires, send a form-encoded request to the discovered token endpoint withgrant_type=refresh_token, client_id, and the current refresh_token. You may include scope to narrow the access token’s granted scope.
Every successful refresh returns a replacement refresh_token. Persist the replacement before using the session again, and never reuse the superseded token. Reuse detection revokes that refresh-token family.
Disconnect access
An application can send its current refresh token in the form-encodedtoken field to the discovered revocation endpoint. This prevents future refresh for that token’s rotation family, but does not revoke other families or pending authorization codes for the same user and client.
A user can disconnect the application from OAuth sessions. This revokes all refresh tokens and pending authorization codes for that user and client pair.
Already-issued access tokens remain usable until their short expiry after either disconnect path. Disabling a registered application prevents new authorization and refresh but does not extend or revoke those access tokens.
For a runnable Node.js CLI or desktop-style implementation, follow Connect an installed TypeScript client with delegated OAuth. OAuth-capable MCP hosts automate the browser journey described in Quickstart with MCP.
Match the credential to the environment
API keys work only in the environment where they were created. Use a testnet key with the testnet API and a production key with the production API. OAuth access tokens carry the issuer of the environment that minted them. Each hosted Agreements API environment validates the issuer configured for that environment, so a token minted by one environment cannot authenticate to the other.
Append
/.well-known/oauth-authorization-server to the issuer to fetch metadata. Treat that metadata as the source of truth for OAuth endpoint URLs.
Testnet access is free and self-service. Production access is available by request. Request production access.
OAuth request scopes
OAuth authorization requests accept these scopes:
Wildcard values are not valid OAuth request scopes.
Account entitlements
The authenticated account’s entitlements apply to API keys and OAuth access tokens. Entitlement matching accepts the four exact scopes above plus these wildcard values:agreements.*webhooks.**
agreements.read token cannot perform an agreement write even if the account has an agreements.write entitlement.
Entitlement modes are:
Common authentication failures
Header casing
Authentication header names are case-insensitive at the HTTP layer, but examples useX-API-Key and Authorization consistently.
Related pages
- Quickstart with TypeScript SDK
- Quickstart with MCP
- Connect an installed TypeScript client with delegated OAuth
- TypeScript client reference
- Errors and troubleshooting
- Use the API Reference group in the sidebar for generated request and response details.