Skip to Content
Using the APIScoped tokens

Scoped tokens

A scoped token gives access to the resources of one customer, for a limited time, and only for what its permissions allow. Unlike an API key, which can do anything in your organization, a scoped token is safe to put in your customers’ hands: your backend creates it, and sends it to a page of your website or to your app, which then calls LicenseManager directly.

For Acme Studio, it’s how the My licenses page of their own website works. When a signed-in photographer opens it, Acme’s backend creates a customer token for them and passes it to the page. The page then lists the photographer’s licenses and deactivates their old laptop by calling LicenseManager itself: Acme’s backend doesn’t relay every request, and the API key never leaves it.

Token types

A token is scoped to a customer, a license or a machine, and reaches:

  • The resource it’s scoped to.
  • The resources above it: a license token reaches its customer.
  • The resources below it: a license token reaches its machines.

But not the resources beside it: a license token doesn’t reach the customer’s other licenses.

TokenCustomerIts licensesTheir machinesUse it for
Customer token✓All of themAll of themAn account page listing everything the customer owns.
License token✓Only its licenseOnly its license’sA page managing one license, like a team admin managing their seats.
Machine token✓Only its machine’s licenseOnly its machineYour app, acting for the device it runs on, like a Sign out of this device button.

A resource the token doesn’t reach answers 404, as if it didn’t exist.

Permissions

Each token holds a list of permissions, its scope. A write permission includes the matching read one.

PermissionAllows
customer:readGetting the customer.
license:readGetting a license, and listing the customer’s licenses.
machine:readGetting a machine, and listing a license’s machines.
machine:writeDeactivating a machine.
customer:write, license:writeNothing yet.

Give each token only what the page needs: a page listing licenses needs license:read, not machine:write.

Create a token

From your backend, with an API key:

curl -X POST https://api.licensemanager.dev/v1/customers/c9Wd3RtE/tokens \ -H "x-api-key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "scope": ["customer:read", "license:read", "machine:write"], "expiresInSeconds": 3600 }'
// 201 Created { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…", "type": "customer", "scope": ["customer:read", "license:read", "machine:write"], "expiresAt": "2026-10-14T11:30:00.000Z" }
EndpointCreates
POST /v1/customers/{id}/tokensA customer token.
POST /v1/licenses/{id}/tokensA license token.
POST /v1/machines/{id}/tokensA machine token.
FieldRequiredWhat it is
scopeYesThe permissions of the token, at least one.
expiresInSecondsNoHow long the token works, from 60 seconds to 365 days. 15 minutes by default.

Requests made with a session of the app can create tokens too, when the member’s role allows it: owners, admins and members, not viewers.

Tokens creating tokens

A token can create tokens for the resources below it, so the page holding it can hand a narrower token on, without going back to your backend:

TokenCan create
Customer tokenLicense tokens of its customer’s licenses, and machine tokens of their machines.
License tokenMachine tokens of its license’s machines.
Machine tokenNothing.

A token can’t give more than it has:

  • Permissions: only the ones it holds. A customer token with license:read and machine:write can create a machine token with machine:read, not one with customer:read. Asking for more answers 403 with insufficient_scope.
  • Expiration: no later than its own. A longer expiresInSeconds is shortened to the creating token’s expiration: check the expiresAt of the response.

For Acme Studio, a team admin’s account page holds a customer token. When the admin opens one of the team’s licenses, the page creates a license token with license:read and machine:write, and passes it to the seat manager widget, which can’t see the team’s other licenses.

Use it in your pages

Create the token on your backend

When a signed-in customer opens the page, find their LicenseManager customer, for example with GET /v1/products/{productId}/customers?email=…, and create a token for them. Keep it short-lived, like the length of a session on your website.

Send it to the page

Return it with the page, or from an endpoint of your backend the page calls. Never send your API key.

Call LicenseManager from the page

The page sends the token as a bearer token, here with the TypeScript SDK. The API accepts cross-origin requests from any website with a token:

import { Configuration, LicensesApi } from "@licenseman/sdk"; const licensesApi = new LicensesApi(new Configuration({ accessToken: token })); const { data: licenses } = await licensesApi.listCustomerLicenses({ customerId });

Create a new token when it expires

Once it expires, requests answer 401 with invalid_token: ask your backend for a new one.

Don’t want to build a page? The customer portal is a ready-made one, running on the same customer tokens, created by portal links.

Endpoints accepting tokens

Only these endpoints accept tokens. Every other one answers 403 with token_not_allowed.

EndpointPermissionToken types
GET /v1/customers/{id}customer:readCustomer, license, machine
GET /v1/customers/{customerId}/licenseslicense:readCustomer
GET /v1/licenses/{id}license:readCustomer, license, machine
GET /v1/licenses/{licenseId}/machinesmachine:readCustomer, license
GET /v1/machines/{id}machine:readCustomer, license, machine
POST /v1/machines/{id}/deactivatemachine:writeCustomer, license, machine
POST /v1/licenses/{id}/tokensAny, within its ownCustomer
POST /v1/machines/{id}/tokensAny, within its ownCustomer, license

Errors

StatusCodeWhen
401invalid_tokenThe token’s signature is invalid, it has expired, or the resource it’s scoped to was deleted.
403token_not_allowedThe endpoint doesn’t accept tokens, or not of this type, like a machine token creating a token.
403insufficient_scopeThe token doesn’t have the permission the endpoint needs, or the ones it tries to give.
403insufficient_roleFrom the app: the member’s role can’t create tokens.
404not_foundThe resource doesn’t exist, or the token doesn’t reach it.

Revoking a token

A token can’t be revoked. It works until it expires, unless the resource it’s scoped to is deleted, which makes it answer invalid_token. Tokens created by another token stay valid too, until their own expiration, which is never later than the token that created them. Create tokens with the shortest duration that works for you.

To cut access right away in an emergency, like a token leaked in a log, you can delete the customer, which first requires deleting their licenses: usually, waiting for the token to expire is the better option.

Security

  • Tokens are signed, so they can’t be changed or forged, but their content can be read by whoever holds them: the resource’s id, the permissions and the expiration.
  • Anyone holding a token can use it: send it over HTTPS only, and keep it out of URLs your servers log. The customer portal removes the token of a portal link from the address bar for that reason.
  • Create tokens for the signed-in customer only. Your backend decides who gets a token: check the customer you create it for is the person signed in to your website.

Edge cases

Test products. Tokens of test products reach test resources, whatever key created them: the live or test mode only limits API keys. A test API key creates tokens of test resources only.

Usage. Requests made with a token, creating a token included, count toward your plan’s API requests, like requests made with an API key, but aren’t rate limited. See rate limits and quotas.

Who did it. What a token does is recorded on the events as Customer token, License token or Machine token.

Machine tokens and validation. A machine token doesn’t replace validating the key: the license token returned by a validation is what your app checks offline. A machine token only lets it call the endpoints above for its machine.