Contents

Offline licences

Your master offline licence, and short-lived licences for your own clients.

The Licensing object

Attributes

  • id required Type: string
  • kind required Type: "licence" "trial"
  • issued_to required Type: string

    Your organisation

  • issued_by required Type: "Unforged"
  • issued_on required Type: string
  • valid_from required Type: string
  • valid_until required Type: string
  • works_until required Type: string

    The last day it works offline: 14 days after valid_until for a licence, while you renew.

  • includes required Type: "verify" "extract" "loupe"[]
  • token optional Type: string

    The licence text (GET /licensing/master only).

  • short_lived required Type: object null

    What you may give your own clients; null when your plan doesn't include short-lived licences.

Licensing on its own page

The ShortLivedLicence object

Attributes

  • id required Type: string
  • for required Type: string

    Who you gave it to. Their licence shows this name.

  • includes required Type: "verify" "extract" "loupe"[]
  • valid_from required Type: string
  • valid_until required Type: string

    The last day it works.

  • status required Type: "scheduled" "active" "ended" "revoked"
  • counts required Type: boolean

    It counts towards your limit (it can still be used offline).

  • created_by required Type: string
  • created_at required Type: string
  • revoked_at required Type: string null
  • revoked_by required Type: string null
  • token optional Type: string

    The licence text (when it is given, and from GET /licensing/licences/{licence_id}).

ShortLivedLicence on its own page

Your offline licensing

GET /licensing

Needs org:read: See org, members, usage

Your master offline licence (without its text) and, when your plan includes them, the short-lived licences you may give your own clients. 404 when your plan has no offline licence.

Returns

  • 200 Type: Licensing
    Your offline licensing
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /licensing · curl
curl "https://api.unforged.sh/licensing" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /licensing · Python
import requests

r = requests.get(
    "https://api.unforged.sh/licensing",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
GET /licensing · Node.js
const res = await fetch('https://api.unforged.sh/licensing', {
  method: 'GET',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());

Your master offline licence

GET /licensing/master

Needs org:settings:manage: Retention, org-wide settings, the API version pin

Your master offline licence with its text, to install or download.

Returns

  • 200 Type: Licensing
    Your master offline licence
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /licensing/master · curl
curl "https://api.unforged.sh/licensing/master" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /licensing/master · Python
import requests

r = requests.get(
    "https://api.unforged.sh/licensing/master",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
GET /licensing/master · Node.js
const res = await fetch('https://api.unforged.sh/licensing/master', {
  method: 'GET',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());

Short-lived licences

GET /licensing/licences

Needs org:read: See org, members, usage

The short-lived licences you have given, newest first, without their text.

Returns

  • 200 Type: ShortLivedLicenceList
    Short-lived licences
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /licensing/licences · curl
curl "https://api.unforged.sh/licensing/licences" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /licensing/licences · Python
import requests

r = requests.get(
    "https://api.unforged.sh/licensing/licences",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
GET /licensing/licences · Node.js
const res = await fetch('https://api.unforged.sh/licensing/licences', {
  method: 'GET',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());

Give a short-lived licence

POST /licensing/licences

Needs org:settings:manage: Retention, org-wide settings, the API version pin

A short-lived licence for one of your clients, with its text. Once your client has it, it keeps working offline until valid_until, even if you revoke it, and it counts towards your limit until then.

Parameters

  • Idempotency-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

Request body

ShortLivedLicenceNew

  • for required Type: string

    Who it is for, such as your client's business name.

  • valid_from required Type: string

    Today or later.

  • valid_until required Type: string

    The last day it works.

  • includes required Type: "verify" "extract" "loupe"[]

    Only what your plan includes.

Returns

  • 201 Type: ShortLivedLicence
    Give a short-lived licence
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 413 Type: Problem
    too_large
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /licensing/licences · curl
curl -X POST "https://api.unforged.sh/licensing/licences" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14" \
  -H "Content-Type: application/json" \
  -d '{}'
POST /licensing/licences · Python
import requests

r = requests.post(
    "https://api.unforged.sh/licensing/licences",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
    json={},
)
print(r.json())
POST /licensing/licences · Node.js
const res = await fetch('https://api.unforged.sh/licensing/licences', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
  body: JSON.stringify({}),
});
console.log(await res.json());

A short-lived licence

GET /licensing/licences/{licence_id}

Needs org:settings:manage: Retention, org-wide settings, the API version pin

One short-lived licence, with its text.

Parameters

  • licence_id pathrequired Type: string

    A short-lived licence's id.

Returns

  • 200 Type: ShortLivedLicence
    A short-lived licence
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /licensing/licences/{licence_id} · curl
curl "https://api.unforged.sh/licensing/licences/{licence_id}" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /licensing/licences/{licence_id} · Python
import requests

r = requests.get(
    "https://api.unforged.sh/licensing/licences/{licence_id}",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
GET /licensing/licences/{licence_id} · Node.js
const res = await fetch('https://api.unforged.sh/licensing/licences/{licence_id}', {
  method: 'GET',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());

Revoke a short-lived licence

POST /licensing/licences/{licence_id}/revoke

Needs org:settings:manage: Retention, org-wide settings, the API version pin

Register lookups stop accepting the licence straight away. A copy on a computer that stays offline keeps working until valid_until, so it still counts towards your limit until then.

Parameters

  • Idempotency-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

  • licence_id pathrequired Type: string

    A short-lived licence's id.

Returns

  • 200 Type: ShortLivedLicence
    Revoke a short-lived licence
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /licensing/licences/{licence_id}/revoke · curl
curl -X POST "https://api.unforged.sh/licensing/licences/{licence_id}/revoke" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
POST /licensing/licences/{licence_id}/revoke · Python
import requests

r = requests.post(
    "https://api.unforged.sh/licensing/licences/{licence_id}/revoke",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
POST /licensing/licences/{licence_id}/revoke · Node.js
const res = await fetch('https://api.unforged.sh/licensing/licences/{licence_id}/revoke', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());