1. Docs
  2. API Walkthrough

API Walkthrough

Take an empty Environment to a passing permission check over the API, with plain curl: an API key, a permission vocabulary, a role, an identity, an assignment, and the evaluation your app runs in production.

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
Create an account. Sign-up asks for your first Application's name and creates it with two Environments, Development and Production. Every resource in this guide lives inside one Environment.Sign in to the Console and pick the Environment you will work in, usually Development, in the Application and Environment switcher above Access Control.Open Access Control → Integrations → API Keys and create a key for the Environment you are working in. The 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 the keys your application checks for: lowercase, dot-separated, in resource.action form such as invoice.read. Each needs a key, a display name and a category; description is optional. 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",    "name": "View invoices",    "category": "Invoices" },
      { "key": "invoice.approve", "name": "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. List the nodes and keep the id of the one whose parent_node_id is null; a new Environment has only the root:

curl -H "X-API-Key: $CANOPY_API_KEY" https://auth.canopy-io.com/api/v1/nodes

# → { "items": [ { "id": "<ROOT_NODE_ID>", "parent_node_id": null, ... } ], "pagination": { ... } }
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. An app-wide answer is not tied to a node, so it names no node and no roles; a node-scoped check also returns effective_node_id and the ids of the roles that granted it in granting_roles:

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",
#       "effective_node_id": null,
#       "granting_roles": [],
#       "denial_reason": null,
#       "deny_id": null
#     }
#   }
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.

Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.