Register a new OAuth client
/portal/v1/accounts/{accountSlug}/applications/{appSlug}/environments/{envSlug}/oauth-clientsRegisters a new confidential OAuth client in the current Environment with the given name, redirect_uris, and optional scopes (defaulting to openid profile email), each one of openid, profile, email, org and permissions; any other value is refused with 400 (oauth.client_scope_unknown). With client_kind: machine it registers a machine client instead: a principal of its own that holds roles (see the assignments routes) and obtains tokens for itself, with an optional organization_node_id (the one organization it acts in), optional allowed_audiences and the permissions scope, the client_credentials grant only and no redirect URIs; it counts toward the plan's machine-credential allowance (409 plan_limit_exceeded). Generates the client_id and a client_secret that is bcrypt-hashed at rest; the raw secret is returned in this response only and is never retrievable again. Responds 201 Created.
Authentication
AuthorizationJWT access token. Never send alongside X-API-Key: a request carrying both is refused.
oauth_clients.manageDeveloper ConsoleCreate, edit, rotate, and delete OAuth clients. Granted through an administrator role in the Admin Workspace; a valid token without it is refused with 403.
Path Parameters
accountSlugstring Required The Account's slug. An unknown one answers 404.
appSlugstring Required The Application's slug within the Account.
envSlugstring Required The Environment's slug within the Application.
Request body
application/json
namestring Required client_kindenum Optional `web` (when omitted): a person signs in through this client with the authorization code grant. `machine`: the client is a principal of its own that holds roles and obtains tokens for itself with the `client_credentials` grant, for the audiences it lists, inside at most one organization; it takes no redirect URIs or sign-in fields.
organization_node_idstring Optional Machine clients only: the organization the client acts in. Its tokens name it and it may hold roles only inside it. Omitted, the client acts across the Environment.
allowed_audiencesstring[] Optional For a machine client: the audiences (your APIs' identifiers) it may request a token for with `audience`, beside the Environment's `m2m_default_audience`. The token's `aud` names the one asked for.
redirect_urisstring[] Optional Required for a web client; a machine client has none.
post_logout_redirect_urisstring[] Optional Where this client may send someone after RP-initiated logout. Matched exactly, and separate from `redirect_uris`: receiving an authorization code and being a landing page after sign-out are different kinds of trust.
scopesstring[] Optional What the client's tokens may carry. Omitted, a web client gets `openid`, `profile` and `email`; a machine client gets `permissions`, the only scope it may hold.
backchannel_logout_uristring Optional Where Canopy delivers a signed logout token, server to server, when a session this client issued tokens for ends. Omit it and the client is told nothing.
invite_redirect_urlstring Optional initiate_login_uristring Optional The route in your application that starts a sign-in (OpenID Connect `initiate_login_uri`): it should send the person to Canopy's `/oauth/authorize`. Canopy's hosted pages that end with nothing to return to, such as an accepted invitation or a verified email, offer it as **Sign in**. Absolute `http` or `https` URL.
Responses
application/json
data *OAuthClientCreatedResponseDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
Errors
When the request can't be completed, the response body includes a stable error code you can branch on, and a correlation_id (also sent as the X-Correlation-Id header). Quote the correlation id when you contact support: it identifies the exact request in Canopy's logs.
account.capability_requiredForbiddenThe signed-in user's administrator roles do not grant the capability this endpoint requires.
Ask an account administrator to grant a role carrying the capability named in the Authentication section, then retry.
Returned object
curl -X POST "https://auth.canopy-io.com/portal/v1/accounts/value/applications/value/environments/value/oauth-clients" \ -H "Authorization: Bearer $CANOPY_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "client_kind": "web", "organization_node_id": "string", "allowed_audiences": [ "https://api.example.com" ], "redirect_uris": [ "string" ], "post_logout_redirect_uris": [ "string" ], "scopes": [ "string" ], "backchannel_logout_uri": "string", "invite_redirect_url": "string", "initiate_login_uri": "string" }'
{ "data": { "id": "string", "client_id": "string", "name": "string", "client_kind": "web", "organization_node_id": "string", "organization_name": "string", "allowed_audiences": [ "string" ], "redirect_uris": [ "string" ], "post_logout_redirect_uris": [ "string" ], "grant_types": [ "string" ], "scopes": [ "string" ], "backchannel_logout_uri": "string", "invite_redirect_url": "string", "initiate_login_uri": "string", "is_active": false, "owner": "developer", "last_token_at": "2026-04-20T12:00:00.000Z", "previous_secret_expires_at": "2026-04-20T12:00:00.000Z", "secret_rotated_at": "2026-04-20T12:00:00.000Z", "version": 0, "created_at": "2026-04-20T12:00:00.000Z", "issuer": "https://auth.yourapp.com", "client_secret": "string" } }
Tell us how we can improve this guide.