1. Docs
  2. Keys & Credentials

Keys & Credentials

The credentials your application uses, which integration scenario needs which, and exactly what goes in your app's configuration. Each scenario below is self-contained: find yours and you have everything you need, no cross-referencing.

Overview

Canopy issues a small set of credentials, and which ones you need depends entirely on what your app does. There are four integration scenarios: pick yours below and you'll find a complete checklist plus a copy-paste configuration. The only value every scenario shares is CANOPY_API_URL (where the Canopy API lives); everything else is specific to the scenario.

The four scenarios:

Hosted login (OAuth2): Canopy's hosted page signs your end-users in, then your backend exchanges an authorization code for tokens. Needs an OAuth client (client_id + client_secret) and a redirect URI.Direct identity auth: your app renders its own login UI and calls Canopy's identity-auth API directly. Needs a publishable key for the Environment.Self-service signup: end-users create their own accounts from your UI via POST /v1/identity/auth/register. Uses the same publishable key as Direct identity auth, with no extra credential; turn on self-signup for the Environment on the Self-signup tab of Authentication in the Developer Console, and the checklist under Direct identity auth covers you.Server-to-server API: backend automation that manages roles, identities, permissions, or evaluates access with no end-user present. Needs an API key.

Hosted login (OAuth2)

Your app redirects the browser to Canopy's hosted login page (/oauth/authorize) using the OAuth 2.0 authorization-code flow with PKCE. After the user signs in, Canopy redirects back to your redirect_uri with a one-time code, and your backend exchanges it for tokens at /oauth/token using the client secret.

When to use: You want Canopy to own the login UI (password handling, email verification, MFA, reset) and just hand your backend a signed JWT.

What you need
CANOPY_API_URL: base URL of the Canopy API (e.g. https://auth.canopy-io.com). Where: your Canopy instance. Not a secret.OAUTH_CLIENT_ID: public identifier of your OAuth client. Where: Developer Console, Access Control → Integrations → OAuth Clients → your client (visible any time), or GET /api/v1/oauth-clients with an API key holding oauth_client.view. Publishable: safe in browsers and redirects.OAUTH_CLIENT_SECRET: backend credential used in the /oauth/token exchange. Where: shown once when you register the client, in the Console or with POST /api/v1/oauth-clients (or after Rotate secret, in either). Secret: backend only, never ship to the browser.OAUTH_REDIRECT_URI: where Canopy returns the user after login. Where: must exactly match one of the client's registered redirect URIs. Not a secret.CANOPY_ISSUER: your Environment's issuer, https://auth.canopy-io.com/environments/{environment_id}, or https://<your auth domain> once a custom auth domain is active. Your backend verifies every token against <issuer>/.well-known/jwks.json and rejects any whose iss differs, or whose aud isn't your OAUTH_CLIENT_ID. Where: Developer Console, Authentication → Signing keys. Not a secret.
Your configuration
# Where Canopy lives (every scenario needs this)
CANOPY_API_URL=https://auth.canopy-io.com

# OAuth client: Access Control → Integrations → OAuth Clients, or POST /api/v1/oauth-clients
OAUTH_CLIENT_ID=<your client_id>
OAUTH_CLIENT_SECRET=<shown once at creation>   # secret, backend only
OAUTH_REDIRECT_URI=https://app.example.com/callback   # must match a registered URI

# Issuer for token verification: Authentication → Signing keys
CANOPY_ISSUER=https://auth.canopy-io.com/environments/<environment_id>   # or https://auth.yourapp.com once a custom auth domain is active

Full walkthrough: Hosted login · register the client under OAuth clients.

Direct identity auth

Your app renders its own sign-in form and posts credentials straight to Canopy's identity-auth endpoints (POST /v1/identity/auth/login, …/register, …/forgot-password, …/reset-password). The same signed JWT comes back: no redirects, no consent screen. The request is keyed to one Environment by its publishable key. When the Environment has self-service signup enabled, the same key also powers browser-direct registration.

When to use: You want full control of the login UI, or to keep users on your domain. It's also the path for flows hosted login can't cover, like native mobile sign-in or custom onboarding.

What you need
CANOPY_API_URL: base URL of the Canopy API (e.g. https://auth.canopy-io.com). Where: your Canopy instance. Not a secret.PUBLISHABLE_KEY: the pk_… key that scopes the identity-auth calls (login, and self-service signup when enabled) to one Environment. Create one per app: Web for a browser app, Native for an installed phone or desktop app, which receives its refresh token in the response body. Where: Developer Console, Access Control → Integrations → Publishable Keys, where keys are created and revoked; or, to copy one, Publishable Key in the ⋯ menu on the Environment's card on the Applications page (visible any time). Publishable: safe to embed in client/browser code; it can't manage anything on its own.Behind a backend proxy (BFF), your backend sends a secret API_KEY in X-API-Key alongside the publishable key, both from the same Environment, and receives the refresh token in the response body so it holds the session itself. See Backend Proxy (BFF).CANOPY_ISSUER: your Environment's issuer, https://auth.canopy-io.com/environments/{environment_id}, or https://<your auth domain> once a custom auth domain is active. Your backend verifies every token against <issuer>/.well-known/jwks.json and rejects any whose iss differs. Where: Developer Console, Authentication → Signing keys. Not a secret.
Your configuration
# Where Canopy lives (every scenario needs this)
CANOPY_API_URL=https://auth.canopy-io.com

# Publishable key: Access Control → Integrations → Publishable Keys, or env card → ⋯ → Publishable Key
PUBLISHABLE_KEY=pk_<your publishable key>   # publishable, safe in client code

# Issuer for token verification: Authentication → Signing keys
CANOPY_ISSUER=https://auth.canopy-io.com/environments/<environment_id>   # or https://auth.yourapp.com once a custom auth domain is active

Full walkthrough: Direct API.

Server-to-server API

Your backend calls the public API (/api/v1/*) on its own behalf, managing roles, permissions, identities, the hierarchy, OAuth clients and webhooks, or evaluating access, with no end-user in the loop. Every request carries an API key in the X-API-Key header. An API key manages Canopy. It is not how a machine authenticates to your API: for that, register a machine client, give it roles, and have it obtain a token with client_credentials, which your API verifies like a user's. Never present one as a substitute for the other.

When to use: Automation, CI scripts, trusted backend services, and anything that runs without a logged-in person.

What you need
CANOPY_API_URL: base URL of the Canopy API (e.g. https://auth.canopy-io.com). Where: your Canopy instance. Not a secret.API_KEY: the cnpy_… key sent as X-API-Key; scoped to one Environment with the permissions you grant it. Where: Developer Console, Access Control → Integrations → API Keys → Create API Key (the value is shown once at creation; rotate to get a new one). Secret: backend only, never commit or expose to a browser.
Your configuration
# Where Canopy lives (every scenario needs this)
CANOPY_API_URL=https://auth.canopy-io.com

# API key: Access Control → Integrations → API Keys
API_KEY=cnpy_<shown once at creation>   # secret, backend only

Full walkthrough: API keys.

Handling secrets safely

Rules that apply everywhere

A few rules apply across every scenario, worth knowing wherever you landed above.

Secret vs publishable. API_KEY (cnpy_) and OAUTH_CLIENT_SECRET are secrets: keep them server-side, out of client code and version control. PUBLISHABLE_KEY (pk_) and OAUTH_CLIENT_ID are publishable: safe to expose in browsers and redirect URLs.Secrets are shown once. Canopy stores only a hash, so a secret is displayed once at creation and cannot be looked up later. Capture it into a secrets manager (Vault, AWS Secrets Manager, a CI secret) immediately. The one repeat: an API key or an OAuth client created through the API with an Idempotency-Key header returns the same response, secret included, if that exact request is retried within 24 hours, so a dropped connection does not lose the secret.Lost a secret? Rotate, don't hunt. Both API keys and OAuth client secrets support Rotate, in the Console or through the API, which mints a new value (the old one stops working immediately) without changing the key's identity, scopes, or audit history.Never commit any of it. Inject secrets via environment variables or a secrets manager. CANOPY_API_URL and redirect URIs are plain config, not secrets, and are fine to keep in source.
Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.