Skip to main content
The Contractors Cloud API supports two ways to authenticate: a long-lived API access token for a single account’s own integrations, and OAuth 2.0 for applications and AI agents that connect on behalf of a Contractors Cloud user without ever seeing their password. Use OAuth when you’re building something other people install and connect to their own Contractors Cloud account — a third-party application, or an AI agent connecting through MCP.

What OAuth is used for

OAuth here follows the standard authorization code grant with PKCE. A user signs in and approves your application once; your application then holds a short-lived access token (plus a longer-lived refresh token) that acts as that user for as long as they stay connected. Every request made with the resulting token is scoped to that one user’s permissions and organization, the same as if they were using the API directly.

Getting a client

OAuth clients for MCP platforms register themselves automatically through dynamic client registration — most AI agents need no manual setup at all. Self-service creation of OAuth clients for general integrations is coming to the Contractors Cloud web app. Until then, contact Contractors Cloud support to have a client provisioned for your integration.

The authorization flow

Request authorization

Redirect the user’s browser to the authorize endpoint:
  • If the user isn’t signed in, they land on the Contractors Cloud login page, then continue on to consent after signing in.
  • If they’re already signed in, they see a consent screen listing your application and the scopes it’s requesting, with Approve and Deny buttons.
  • If they’ve previously approved this exact client and scope combination, the consent screen is skipped and they’re redirected straight back to your redirect_uri with a fresh authorization code.

The callback

On approval, the user’s browser is redirected to your redirect_uri with a code and the state you sent:
If the user denies the request instead, the redirect carries error=access_denied. Verify the returned state matches what you sent before you do anything else with the code.

Exchange the code for tokens

Treat client_secret and refresh_token values as credentials — store them the way you’d store any other secret. Never expose them in client-side code or commit them to a public repository.

Using the access token

Include the access token as a bearer token on every API request:

Refreshing an access token

When the access token expires, exchange the refresh token for a new pair instead of asking the user to sign in again:
The response has the same shape as the initial token exchange. Both the access token and the refresh token are rotated on every refresh — the old refresh token is invalidated, so store the new one and discard the old.

Token lifetimes

Scopes

Request the minimal scopes you need — only ask for the read, write, or mcp:use scopes your application actually uses.

PKCE for public clients

Public clients — those without a client secret — must use PKCE (RFC 7636) on every authorization request:
  1. Generate a random code_verifier: 43–128 characters from A-Z, a-z, 0-9, -, ., _, ~.
  2. Derive code_challenge = base64url(sha256(code_verifier)) and send it, along with code_challenge_method=S256, on the authorization request.
  3. Send the original code_verifier (not the challenge) in the token exchange, in place of a client_secret.
Only S256 is accepted — plain (or an omitted code_challenge_method) is rejected with an invalid_request error, as is an authorization request from a public client with no code_challenge at all. Confidential clients (those with a secret) can use PKCE too, as defense in depth, but it’s optional for them.

Dynamic client registration

MCP clients that discover the registration_endpoint from discovery can self-provision an OAuth client automatically, with no manual setup:
Every dynamically registered client is public — no secret is issued, so PKCE is required on every authorization request — and is limited to the mcp:use scope. A client registered this way can connect an AI agent through MCP, but can’t request read or write access. redirect_uris must use callback addresses from approved platforms — contact support to add yours. Native clients (such as Claude Code) that receive the OAuth callback on a loopback address use an ephemeral port chosen at connect time, so the exact port isn’t known when the client registers. A loopback redirect URI (http://localhost, http://127.0.0.1) matches regardless of port — register once, and connect using whichever port your client happens to bind that session.

Discovery

Both are unauthenticated and let a compliant client bootstrap the OAuth flow without any out-of-band configuration. An unauthenticated request to the MCP endpoint returns 401 with a WWW-Authenticate header pointing at the protected-resource metadata URL:

Client credentials grant

The client-credentials grant isn’t a supported integration path for this API. It doesn’t identify a user, and every endpoint here requires a token that acts on behalf of a specific person. Use the authorization flow above, even for server-to-server integrations, so requests always run as a real user in the target organization.

Errors

OAuth error responses follow the RFC 6749 format:
See Connect your AI agent for the end-user walkthrough of connecting Claude, ChatGPT, and other MCP clients over this flow.