SDKs and Libraries
Official client libraries for calling the Canopy API, plus the REST and OpenAPI route for every other language.
Official SDKs
Maintained by the Canopy team. They wrap the response envelope, pagination and retries so you do not hand-roll HTTP calls, and they answer token verification and permission checks inside your own process, so neither costs a call to Canopy on every request.
Requires Node 18 or later, and runs unchanged anywhere fetch exists: browsers, Cloudflare Workers, Deno. Ships ESM and CommonJS with generated types and zero runtime dependencies.
A module that registers the client as an injectable provider and a guard that evaluates a permission on a route, so authorization is a decorator rather than a call in every handler. Three scopes: node, app_wide, and org, which decides inside the organization the caller's token is acting in. Requires NestJS 10 or 11.
For AI coding assistants
The Canopy agent skill teaches an AI coding assistant to integrate with these SDKs the way these docs describe: choosing the sign-in shape, verifying tokens with the right audience, authorizing in-process, and failing closed. It lives in the SDK repository and is released with the packages.
Other assistants read the same Markdown files directly: point them at SKILL.md, or at the repository's AGENTS.md, which links to it.
What the SDKs handle for you
Writing a fetch call against a documented REST API is straightforward. These are the parts that are easy to get subtly wrong, and they are why the hand-written layer exists.
No SDK for your language?
Every Canopy capability is available over plain HTTP. The API is described by an OpenAPI specification published at a stable URL, so you can generate a typed client for any language your toolchain supports, or call the endpoints directly.
Feed this document to a generator and get a typed client in the language you build in. It is the same specification that renders Canopy's own API reference, so it never drifts from the live API.
Or skip the generator. Every endpoint is a plain HTTP call.
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": "7d3f9b2e-4c1a-4e8b-9f2d-1a6c5e0b8f34",
"permission": "orders.refund",
"scope": "node",
"node_id": "e6b2d8f4-9a1c-4d7e-8b3f-2c5a9e1d6f48"
}'Always send a scope
scope is required. Use node to ask whether the identity holds the permission at node_id, walking the lineage so a grant on an ancestor is inherited. Use app_wide for the coarse “anywhere in this Environment” question: useful for hiding a menu item, never for guarding a resource that belongs to a node.
The answer names the grant roots and, up to max_nodes, every node beneath them, or env_wide when every node qualifies. Filter your own table by it. The SDK computes the same answer from its cache; this is the fresh one, for a process that does not run the SDK.
curl "https://auth.canopy-io.com/api/v1/identities/7d3f9b2e-4c1a-4e8b-9f2d-1a6c5e0b8f34/accessible-nodes?permission=orders.view" \ -H "X-API-Key: $CANOPY_API_KEY"
Versioning
The SDK follows semantic versioning. While it is on a 0.x release the surface may still change between minor versions, so pin the minor if you need that stability. The generated types are checked against the published specification on every pull request, before every release, and weekly in between, so drift is reported rather than found by a consumer. A run that cannot reach the specification skips the check with a warning rather than failing. The local authorizer is held to the API the same way: its suite replays the authorization conformance cases Canopy publishes at /conformance/authorization.json, the cases the API's own suite replays. The grant and parent-edge responses it evaluates from carry a model_version, and an SDK that does not know the version it is served refuses with CanopyAuthorizerError rather than guessing, so an older SDK never allows what the API has started to deny.
Tell us how we can improve this guide.