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.
| Token | Customer | Its licenses | Their machines | Use it for |
|---|---|---|---|---|
| Customer token | ✓ | All of them | All of them | An account page listing everything the customer owns. |
| License token | ✓ | Only its license | Only its license’s | A page managing one license, like a team admin managing their seats. |
| Machine token | ✓ | Only its machine’s license | Only its machine | Your 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.
| Permission | Allows |
|---|---|
customer:read | Getting the customer. |
license:read | Getting a license, and listing the customer’s licenses. |
machine:read | Getting a machine, and listing a license’s machines. |
machine:write | Deactivating a machine. |
customer:write, license:write | Nothing 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"
}| Endpoint | Creates |
|---|---|
POST /v1/customers/{id}/tokens | A customer token. |
POST /v1/licenses/{id}/tokens | A license token. |
POST /v1/machines/{id}/tokens | A machine token. |
| Field | Required | What it is |
|---|---|---|
scope | Yes | The permissions of the token, at least one. |
expiresInSeconds | No | How 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:
| Token | Can create |
|---|---|
| Customer token | License tokens of its customer’s licenses, and machine tokens of their machines. |
| License token | Machine tokens of its license’s machines. |
| Machine token | Nothing. |
A token can’t give more than it has:
- Permissions: only the ones it holds. A customer token with
license:readandmachine:writecan create a machine token withmachine:read, not one withcustomer:read. Asking for more answers403withinsufficient_scope. - Expiration: no later than its own. A longer
expiresInSecondsis shortened to the creating token’s expiration: check theexpiresAtof 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.
| Endpoint | Permission | Token types |
|---|---|---|
GET /v1/customers/{id} | customer:read | Customer, license, machine |
GET /v1/customers/{customerId}/licenses | license:read | Customer |
GET /v1/licenses/{id} | license:read | Customer, license, machine |
GET /v1/licenses/{licenseId}/machines | machine:read | Customer, license |
GET /v1/machines/{id} | machine:read | Customer, license, machine |
POST /v1/machines/{id}/deactivate | machine:write | Customer, license, machine |
POST /v1/licenses/{id}/tokens | Any, within its own | Customer |
POST /v1/machines/{id}/tokens | Any, within its own | Customer, license |
Errors
| Status | Code | When |
|---|---|---|
401 | invalid_token | The token’s signature is invalid, it has expired, or the resource it’s scoped to was deleted. |
403 | token_not_allowed | The endpoint doesn’t accept tokens, or not of this type, like a machine token creating a token. |
403 | insufficient_scope | The token doesn’t have the permission the endpoint needs, or the ones it tries to give. |
403 | insufficient_role | From the app: the member’s role can’t create tokens. |
404 | not_found | The 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.