AI Quickstart
Integrating Canopy with an AI coding agent? Point it at the machine-readable surfaces below, then walk the API from an empty Environment to a passing permission check.
Machine-readable surfaces
Point your agent at these. Everything the docs describe is also published in formats an agent can fetch directly, with no browser and no rendering:
Don't guess documentation URLs
Endpoint pages live at /docs/api-reference/{operationId}, for example /docs/api-reference/ApiPermissionsController_evaluate. The ids come from the OpenAPI documents; resource-name URLs like /docs/api-reference/permissions do not exist.
Pick a session mode
The walkthrough below provisions an Environment over the API, which is a server-to-server job and needs no decision here. Signing your end users in from a browser does, and it is the one part that cannot be inferred from the endpoint catalog, because every mode calls the same endpoints:
Decide this before you write sign-in code
This is worth deciding before you write sign-in code rather than after. Nothing here fails at build time: curl and a same-origin dev server behave perfectly. A third-party refresh cookie only breaks in a real browser on your production domain, where the user is signed out as soon as the 15-minute access token lapses.
Prerequisites
Provisioning starts in the Developer Console, where you create your credentials. Everything after these three steps can be driven over the API.
In the Console
cnpy_-prefixed secret is shown exactly once. Store it as CANOPY_API_KEY.API keys are Environment-scoped: a key created in Development can never touch Production. The full credential map (which integration scenario needs which credential) is on Keys & Credentials.
API walkthrough
This takes an empty Environment to a passing permission check. Every request goes to your API origin with the key in the X-API-Key header. Single-resource responses arrive as { "data": ... }, collections as { "items": [...] }, and errors as { "error": ... }, the same envelope on every endpoint. The steps use plain curl so they work from any language; on Node.js or NestJS, the official SDKs wrap the auth header, envelope, retries, and pagination for you.
1. Register your permission vocabulary
Permissions are free-form dot-notation keys your application checks for. Register them in batches:
curl -X POST https://auth.canopy-io.com/api/v1/permissions \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "permissions": [ { "key": "invoice.read", "description": "View invoices", "category": "Invoices" }, { "key": "invoice.approve", "description": "Approve invoices", "category": "Invoices" } ] }'
2. Create a role and attach permissions
Create the role first (its id comes back in data.id), then set the permission keys it grants:
curl -X POST https://auth.canopy-io.com/api/v1/roles \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Invoice Manager", "description": "Read and approve invoices" }' curl -X PUT https://auth.canopy-io.com/api/v1/roles/$ROLE_ID/permissions \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "permission_keys": ["invoice.read", "invoice.approve"] }'
3. Create an identity
The end user being authorized. Keep the returned data.id:
curl -X POST https://auth.canopy-io.com/api/v1/identities \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "ada@acme.com", "first_name": "Ada", "last_name": "Lovelace" }'
4. Find the root hierarchy node
Every Environment starts with a root node, and assignments attach at a node. Fetch the tree and keep the root's id:
curl -H "X-API-Key: $CANOPY_API_KEY" https://auth.canopy-io.com/api/v1/nodes # → { "data": { "tree": [ { "id": "<ROOT_NODE_ID>", ... } ] } }
5. Assign the role at the root
Assignments cascade to every descendant of their node, so assigning at the root grants the role everywhere, exactly what a flat-RBAC setup wants:
curl -X POST https://auth.canopy-io.com/api/v1/assignments \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "identity_id": "$IDENTITY_ID", "role_id": "$ROLE_ID", "node_id": "$ROOT_NODE_ID" }'
6. Evaluate: the question your app asks in production
scope: "app_wide" asks “does this identity hold the permission anywhere?”; scope: "node" with a node_id anchors the check to one part of the tree:
curl -X POST https://auth.canopy-io.com/api/v1/permissions/evaluate \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "identity_id": "$IDENTITY_ID", "permission": "invoice.approve", "scope": "app_wide" }' # → { # "data": { # "allowed": true, # "permission": "invoice.approve", # "scope_evaluated": "app_wide", # "granting_roles": ["Invoice Manager"] # } # }
7. Optional: subscribe to change events
Webhooks fire on catalog event types: the strings must match the catalog verbatim, or use the wildcard "*". Fetch the full catalog live from GET /api/v1/webhooks/event-types, or read Webhook Event Types:
curl -X POST https://auth.canopy-io.com/api/v1/webhooks \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks/access-changes", "event_types": ["assignment.created", "assignment.removed"] }'
That's the whole loop: vocabulary, role, identity, assignment, evaluation. From here, the API Reference documents every endpoint the specs enumerate, and Webhook Event Types lists every event string you can subscribe to.
Tell us how we can improve this guide.