API reference
Management API
Privileged endpoints for your own backend: create and manage end users, grant time, generate and revoke license keys, and manage variables.
Server-side only
Management keys are secrets. Never embed ak_dev_… or ak_live_… keys in software you distribute, in a browser, or in a public repository. If a key leaks, revoke it in the dashboard and create a new one.
Authentication
Create a secret key for an application in the dashboard. Each key belongs to exactly one application, carries a set of scopes, and is stored by Aroli only as a hash - copy it when it's shown. Send it as a bearer token:
curl https://<your-aroli-host>/api/v1/management/users \
-H "Authorization: Bearer ak_live_…"| Scope | Grants | Used by |
|---|---|---|
| users:read | read | GET /users, GET /users/{id} |
| users:write | write | Create, update, delete, ban, unban, revoke sessions, reset devices |
| licenses:read | read | GET /licenses |
| licenses:write | write | Generate and revoke license keys |
| entitlements:write | write | POST /users/{id}/grants |
| variables:read | read | GET /variables |
| variables:write | write | PUT /variables/{key} |
A request with a key that lacks the required scope fails with 403 forbidden. Requests are rate-limited per key; see the X-RateLimit-* headers.
Idempotency
POST, PATCH and DELETE requests accept an Idempotency-Key header. If you retry with the same key and the same request, Aroli returns the original response (with Idempotent-Replayed: true) instead of performing the action again. Reusing a key with a different request fails with idempotency_conflict.
curl -X POST https://<your-aroli-host>/api/v1/management/licenses \
-H "Authorization: Bearer ak_live_…" \
-H "Idempotency-Key: batch-2026-11-launch" \
-H "Content-Type: application/json" \
-d '{ "product": "pro", "quantity": 50, "durationDays": 30, "maxActivations": 3, "prefix": "NWS" }'Responses
Like the client API, successful responses are { data, requestId } and errors are { "error": { "code", "message" }, "requestId" }. Management responses are not Ed25519-signed - they travel between your server and Aroli over HTTPS.
Users
Base path: /api/v1/management.
/usersusers:read · users:writeList end users of the key's application, or create one (username, password, optional email).
/users/{id}users:read · users:writeFetch, update or delete a single end user.
/users/{id}/banusers:writeBans the user. Optional body { reason }. Banned users can't sign in (user_banned).
/users/{id}/unbanusers:writeLifts a ban.
/users/{id}/sessions/revokeusers:writeEnds all of the user's active sessions immediately.
/users/{id}/devices/resetusers:writeUnbinds all of the user's devices so they can sign in from a new machine.
/users/{id}/grantsentitlements:writeGrants time on a product directly, without a license key.
| Field | Type | Description |
|---|---|---|
| product | string | Product key, e.g. pro. |
| durationDays | number | null | Days to add, or null for lifetime. |
| note | string? | Optional note stored with the grant. |
Licenses
/licenseslicenses:readLists license keys for the application. Keys are returned with a short hint - never the full plaintext.
/licenseslicenses:writeGenerates a batch of keys. The plaintext keys are returned once, in this response only - store or deliver them immediately.
| Field | Type | Description |
|---|---|---|
| product | string | Product key the licenses grant. |
| quantity | number | How many keys to generate. |
| durationDays | number | null | Time each key grants, or null for lifetime. |
| maxActivations | number | How many devices each key may be activated on. |
| label | string? | Optional label to find the batch later. |
| prefix | string? | Optional key prefix, up to 8 letters or digits (e.g. NWS). |
/licenses/{id}/revokelicenses:writeRevokes a key. Only the time that this key granted is withdrawn from the user's entitlement - see revocation.
Variables
/variablesvariables:readLists the application's variables.
/variables/{key}variables:writeCreates or replaces a variable. Body { value, scope }, where scope is PUBLIC (readable with the app ID), AUTHENTICATED (requires a signed-in user) or PRIVATE (never served to clients).
OpenAPI
A machine-readable description is published at /api/v1/openapi.json. You can use it to generate a typed client for your backend.