Tenant-scoped API requests need a bearer token and the tenant identified by the endpoint. Most modules take X-Tenant-Id; some Account management routes take the tenant in the URL instead. Public endpoints have their own rules.

Personal Access Tokens

  1. Open Settings → Developers. This tab requires account:api:access.
  2. Under Create API Token, enter a name, choose a 7-, 30- or 90-day expiry, and select the permissions needed by the integration.
  3. Click Create Token and copy the token immediately: it is shown only once.
  4. Send it as Authorization: Bearer <token> and send the current workspace’s Tenant ID from Settings → User.

You can select permissions you hold in that workspace. Keep the token private. To replace it, create a new token, update your integration, then Revoke the old token from Active Tokens. Revoked or expired credentials must be replaced.

ThirdSectorBee supports authorisation-code exchange. There is no documented client_credentials grant: registering an application does not by itself authorise it to access user data.

For a confidential application:

  1. Register it using POST /v1/account/tenants/{tenantId}/oauth-clients, with an authenticated user who has account:api:access. Supply name, exact redirectUris and requiredPermissions as described in the Account API.
  2. Store the returned clientId and clientSecret securely. The secret is returned only at creation.
  3. Send the user to the application’s consent page:

    https://app.thirdsectorbee.com/oauth/authorize?client_id=<clientId>&redirect_uri=<encoded-registered-uri>&state=<random-state>
    
  4. The user signs in and approves access. At your registered callback, verify that state matches the value you sent and retrieve the single-use code.
  5. Exchange the code from your server. This flow uses a JSON body:

    curl https://api.thirdsectorbee.com/v1/account/oauth/token \
      -H 'Content-Type: application/json' \
      --data @oauth-code-exchange.json
    

    Create oauth-code-exchange.json securely with these fields, replacing the example values:

    {
      "client_id": "your-client-id",
      "client_secret": "your-client-secret",
      "code": "code-from-callback",
      "redirect_uri": "https://your-service.example/callback"
    }
    

The confidential-client flow returns an access token with a 90-day lifetime. It does not issue the connector flow’s rotating refresh token. Arrange renewed authorisation before expiry; use the returned expiry information rather than assuming tokens last indefinitely.

Connector applications: PKCE and refresh tokens

Explicitly registered connector clients (connectorPlatform: claude or chatgpt) use a separate public-client contract. Registration includes the configured API resource. Their consent request must include response_type=code, that exact resource, requested scope, client_id, the registered redirect_uri, state, and a PKCE code_challenge with code_challenge_method=S256. Connector consent also requires the workspace’s LLM integration feature to be enabled; otherwise the consent page rejects the request.

Exchange their code with application/x-www-form-urlencoded using grant_type=authorization_code, client_id, resource, code, code_verifier and the exact redirect_uri. A refresh uses grant_type=refresh_token, client_id, resource and refresh_token. Do not send the confidential-client JSON example for these clients.

Connector access tokens last at most one hour; refresh tokens rotate and share a 30-day absolute grant lifetime. Replace the stored refresh token after each successful refresh. Reusing an old refresh token revokes the grant. See the Account API for schemas and supported scopes.

Make an authenticated request

For example, listing programmes requires the Programmes module and programmes:read:programmes:

curl https://api.thirdsectorbee.com/v1/programmes/programmes \
  -H "Authorization: Bearer $TSB_TOKEN" \
  -H "X-Tenant-Id: $TSB_TENANT_ID"

A workspace is a tenant; an organisation may contain several. Use the intended workspace’s ID. Access is checked against current membership and permissions, and connector credentials are additionally restricted by the consented grant.

Diagnose authentication failures

Response Next step
401 Check that the bearer header is present and the credential is valid
403 with {"message":"Forbidden"} from the Account authorizer The presented token may be expired or invalid; obtain a valid credential
403 with nested error.code: FORBIDDEN Check workspace membership, required permissions and the token’s permitted scope
400 at token exchange Check the request format, registered callback, code/PKCE verifier and resource; codes cannot be reused

Do not assume every 403 means a missing permission. Error formats vary by endpoint; consult the module reference.

Public endpoints

Engage’s public form endpoints and Account’s token exchange do not require a user bearer header. Their form/embed credentials or OAuth exchange parameters still apply. Follow each operation’s reference rather than removing authentication from a tenant-scoped API.