Skip to content

API Reference

Base URL: https://api.xp-flightdeck.com/licenses-api/v1
Always send Content-Type: application/json for POST bodies.


GET /health

Description: Service health probe.
Auth: none
200

{ "ok": true }
cURL
curl -sS https://api.xp-flightdeck.com/licenses-api/v1/health


GET /public-key

Description: Returns the currently active Ed25519 public key (and its kid) used to sign new tokens.
Auth: none
200

{ "kid": "a1b2c3d4", "ed25519_public_key_b64": "BASE64..." }
cURL
curl -sS https://api.xp-flightdeck.com/licenses-api/v1/public-key


GET /public-keys

Description: Returns every signing key — active and retired — so a token can still be verified by its kid after a rotation.
Auth: none
200

[
  { "kid": "a1b2c3d4", "public_key_b64": "BASE64...", "active": true,  "created_at": "2026-07-08T10:00:00Z", "retired_at": null },
  { "kid": "9f8e7d6c", "public_key_b64": "BASE64...", "active": false, "created_at": "2026-01-01T00:00:00Z", "retired_at": "2026-07-08T10:00:00Z" }
]
cURL
curl -sS https://api.xp-flightdeck.com/licenses-api/v1/public-keys


POST /activate

Description: Activate a license on a device, scoped to the calling publisher. Rate-limited per (publisher, email). Enforces enabled status, email ownership, and activation quota.
Auth header: X-Client-Token (this publisher's)
Required fields: email, license_key, device_hash, device_name
Body

{
  "email": "pilot@example.com",
  "license_key": "ABCD-EF23-GH45",
  "device_hash": "sha256-of-device",
  "device_name": "My PC"
}
200
{ "token": { "...": "see Guides for structure", "kid": "a1b2c3d4", "sig": "..." }, "mode": "full" }
Errors: 400 wrong email for license · 403 license disabled · 404 not found · 409 activation limit reached · 429 too many attempts for this end user, retry after RATE_LIMIT_WINDOW_SECONDS
cURL
curl -sS -X POST   -H "X-Client-Token: $CLIENT_TOKEN"   -H "Content-Type: application/json"   https://api.xp-flightdeck.com/licenses-api/v1/activate   -d '{"email":"pilot@example.com","license_key":"ABCD-EF23-GH45","device_hash":"<sha256>","device_name":"PC"}'


POST /deactivate

Description: Deactivate a specific device for a license so the slot can be reused.
Auth header: X-Client-Token
Required fields: license_key_hash, device_hash
Body

{ "license_key_hash": "<64-hex>", "device_hash": "sha256-of-device" }
200
{ "message": "device deactivated", "updated": 1, "license_ref": "GH45" }
If the license is disabled:
{ "message": "license disabled", "license_ref": "GH45", "updated": 0 }
Errors: 404 license not found · 422 invalid license_key_hash format
cURL
curl -sS -X POST   -H "X-Client-Token: $CLIENT_TOKEN"   -H "Content-Type: application/json"   https://api.xp-flightdeck.com/licenses-api/v1/deactivate   -d '{"license_key_hash":"<64-hex>","device_hash":"<sha256>"}'


POST /admin/licenses

Description: Create a license for a user. The clear license key is returned once.
Auth header: X-Admin-Token
Required fields: email, product, edition, max_activations
Optional fields: features (array of strings)
Body

{
  "email": "pilot@example.com",
  "product": "YourProduct",
  "edition": "Pro",
  "max_activations": 3,
  "features": ["featA", "featB"]
}
201/200
{
  "license_key": "ABCD-EF23-GH45",
  "max_activations": 3,
  "created_at": "2025-09-01T10:00:00Z",
  "product": "YourProduct",
  "edition": "Pro",
  "features": ["featA","featB"]
}
cURL
curl -sS -X POST   -H "X-Admin-Token: $ADMIN_TOKEN"   -H "Content-Type: application/json"   https://api.xp-flightdeck.com/licenses-api/v1/admin/licenses   -d '{"email":"pilot@example.com","product":"YourProduct","edition":"Pro","max_activations":3,"features":["featA","featB"]}'


GET /admin/licenses

Description: List licenses with activation details, scoped to the calling publisher.
Auth header: X-Admin-Token (this publisher's)
Query params (all optional): email, product, edition, enabled, limit (default 50, max 200), offset (default 0)
200{ items, total }

{
  "items": [{
    "license_key_hash": "e3b0c442...64hex",
    "license_ref": "GH45",
    "email": "pilot@example.com",
    "product": "YourProduct",
    "edition": "Pro",
    "enabled": true,
    "active_devices_count": 1,
    "max_activations": 3,
    "created_at": "2025-09-01T10:00:00Z",
    "activations": [
      { "device_hash":"<sha256>", "device_name":"My PC", "deactivated": false, "activated_at": "2025-09-01T10:10:00Z" }
    ]
  }],
  "total": 1
}
cURL
curl -sS   -H "X-Admin-Token: $ADMIN_TOKEN"   "https://api.xp-flightdeck.com/licenses-api/v1/admin/licenses?email=pilot@example.com&enabled=true&limit=50&offset=0"


POST /admin/licenses/enable

Description: Enable/disable a license.
Auth header: X-Admin-Token
Required fields: license_key, enabled, email
Body

{ "license_key": "ABCD-EF23-GH45", "enabled": true, "email": "pilot@example.com" }
200
{ "message": "license enabled", "enabled": true, "license_ref": "GH45" }
If already in desired state:
{ "message": "already enabled", "enabled": true, "license_ref": "GH45" }
cURL
curl -sS -X POST   -H "X-Admin-Token: $ADMIN_TOKEN"   -H "Content-Type: application/json"   https://api.xp-flightdeck.com/licenses-api/v1/admin/licenses/enable   -d '{"license_key":"ABCD-EF23-GH45","enabled":true,"email":"pilot@example.com"}'


POST /admin/deactivate-device

Description: Force-deactivate a device for a license (admin override).
Auth header: X-Admin-Token
Required fields: license_key, device_hash, email
Body

{ "license_key": "ABCD-EF23-GH45", "device_hash": "<sha256>", "email": "pilot@example.com" }
200
{ "message": "device deactivated", "updated": 1, "license_ref": "GH45" }
cURL
curl -sS -X POST   -H "X-Admin-Token: $ADMIN_TOKEN"   -H "Content-Type: application/json"   https://api.xp-flightdeck.com/licenses-api/v1/admin/deactivate-device   -d '{"license_key":"ABCD-EF23-GH45","device_hash":"<sha256>","email":"pilot@example.com"}'


Platform endpoints (operator only)

These are not for publishers — they're how the platform operator creates and manages publisher accounts, and rotates the platform-wide signing key. All require X-Platform-Token.

POST /platform/publishers

Description: Create a publisher (tenant). Returns client_token and admin_token once — they are never recoverable afterward, only their hash is stored.
Auth header: X-Platform-Token
Required fields: name, slug (lowercase, alphanumeric + hyphens, 2-50 chars)
Body

{ "name": "Your Studio", "slug": "your-studio" }
200
{
  "id": 1,
  "name": "Your Studio",
  "slug": "your-studio",
  "client_token": "shown-once...",
  "admin_token": "shown-once...",
  "created_at": "2026-07-08T10:00:00Z"
}
Errors: 409 slug already exists · 422 invalid slug/name


GET /platform/publishers

Description: List publishers (never returns tokens or their hashes).
Auth header: X-Platform-Token
200

[{ "id": 1, "name": "Your Studio", "slug": "your-studio", "enabled": true, "created_at": "2026-07-08T10:00:00Z" }]


POST /platform/publishers/enable

Description: Enable or disable a publisher account — a kill switch that blocks all of that publisher's client/admin calls immediately.
Auth header: X-Platform-Token
Required fields: slug, enabled
Body

{ "slug": "your-studio", "enabled": false }


POST /platform/publishers/rotate-token

Description: Rotate a publisher's client or admin token. The old token stops working immediately — the publisher must update whatever embeds it (app build, back-office tool).
Auth header: X-Platform-Token
Required fields: slug, which ("client" or "admin")
Body

{ "slug": "your-studio", "which": "client" }
200
{ "slug": "your-studio", "which": "client", "token": "shown-once..." }


POST /platform/keys/rotate

Description: Rotate the Ed25519 signing key used for all publishers' tokens. The retired key is kept (not deleted) so tokens already issued keep verifying via GET /public-keys until they expire.
Auth header: X-Platform-Token
200

{ "kid": "b2c3d4e5", "public_key_b64": "BASE64...", "created_at": "2026-07-09T09:00:00Z" }