1. Docs
  2. SDKs and Libraries

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.

Node.js and TypeScript

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.

npm install @canopy-io/node
NestJS

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.

npm install @canopy-io/nestjs

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.

Verifying an access token. Canopy signs tokens RS256 and publishes the keys, so the SDK checks them in-process: one key fetch, then no network. The traps are why it ships rather than being left to each caller: pinning the algorithm so alg:none and HMAC key-confusion cannot get in, bounding refetches so a forged key id cannot hammer the issuer, refusing to ignore an audience the token carries, and refusing a pre-auth token: genuine and correctly signed, but issued before the user picked an Account. Answering permission checks without a call per request. Rather than asking “may this identity act here” every time, the SDK asks “where may it act” once and walks your hierarchy in memory. A grant already means a node and everything beneath it, so what is cached stays small even for an administrator granted near the root, and the hierarchy is revalidated on the same short window as the grants, because moving a node changes what an inherited grant reaches even though no grant changed. Retry safety. GET, HEAD, PUT and DELETE are idempotent by HTTP definition and are retried on a 5xx. POST is not, so a blind retry there could create a second role assignment. A 429 is always retried, because the request was refused before anything happened. Two pagination styles behind one shape. The audit log is cursor-paginated and everything else is offset, but the top-level response looks identical either way, so a hand-rolled loop can silently read only the first page, or never terminate. The response envelope. Single items arrive under data, collections under items, paginated collections with a pagination block, partial success as a summary plus per-item results, failures under error, and deletes as a bare 204. Typed error codes. Branching on a stable code such as rbac.assignment_conflict survives a message being reworded.

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.

OpenAPI specification

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.

/openapi/api.json
Check a permission with curl

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": "idn_8f2c",
        "permission": "orders.refund",
        "scope": "node",
        "node_id": "nod_sf01"
      }'
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.

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 build, so a release cannot quietly describe an API that has moved on.

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

Tell us how we can improve this guide.