Server endpoints

The endpoints your backend calls with a secret key, to issue, read and change keys, free devices, report releases and make test licenses.

… stands for /v1/licenses. See also errors common to every endpoint and rules the change endpoints share.

EndpointDoes
Issue and read
POST /v1/licensesIssues a key, or several as one sale.
GET /v1/licenses/:keyReturns one license.
GET /v1/licensesLists your licenses, with filters.
GET /v1/checkouts/:session/licenseReturns the license a Stripe checkout or a Paddle transaction created or changed.
POST …/:key/fileReturns a key's key file or license file.
Change a key
POST …/:key/extendMoves its expiry later.
POST …/:key/versionsSets the versions it covers and its upgrade protection.
POST …/:key/devicesSets its device limit.
POST …/:key/seat-releaseSets when its unused seats are freed.
POST …/:key/user-lockSets who can activate it.
…/:key/meters/:meterSets, removes, tops up, resets or spends a meter.
POST …/:key/revokeRevokes it.
Devices
GET …/:key/activationsLists the devices it is active on.
DELETE …/:key/activations/:idFrees one device's seat.
Products
POST /v1/products/:slug/releasesReports a release.
GET /v1/productsLists your products and their license models.
Testing
POST /v1/test-licensesMakes a test license in a chosen state.

Authentication

  • Send Authorization: Bearer <secret>, with a secret key from the dashboard's API keys page.
  • A missing, invalid or revoked secret key returns 401 unauthorized.
  • Each secret key can make 1,200 requests per minute, of which at most 600 may issue licenses or make test licenses (see Rate limits).
  • kg_live_: reaches every license in your account and issues live licenses.
  • kg_test_: issues test licenses and reaches only those. A live license or a live checkout gets the same response as one that does not exist.

A license key in a path is case-insensitive, and responses return the key as it was issued.

Errors on every endpoint with :key

404 unknown-license

No license in your account has that key, or a test secret key asked for a live one.

Rules the change endpoints share

RuleHolds for
Sending what the key already has returns it unchanged, so retries are safe. extend, devices, seat-release, user-lock, and setting a meter
The change appears in the key's history on the dashboard. devices, seat-release, user-lock, every meter route (as made over the API), and each file download
A revoked key takes the change and stays revoked. devices, seat-release, the meter routes except a spend (a revoked key refuses a spend)

POST /v1/licenses

Issues a license key, or several as one sale.

Request

Example
{  "product": "my-product",  "modelId": "mod_…",  "email": "[email protected]",  "version": "2.4.1",  "sendEmail": true}
product

Required. The product's API ID.

modelId

The license model sold, by its ID (mod_ and 32 hex characters). It sets the type, expiry, device limit, grace, Offline days, Free unused seats, entitlements, versions and price.

model

perpetual, subscription, trial or timed, for a key sold under no model (see Keys sold under no model). Send modelId or model; if you send both, modelId takes precedence.

  • A timed one needs expiresAt (400 term-required without it): only a timed model names a term.
  • It takes the device limit, grace, entitlements, Offline days and Free unused seats of the product's oldest model of that type, or 3 devices, no grace and no seat release if there is none.
  • It has no price, and no expiry unless you send expiresAt.
  • Its versions are its type's default: a perpetual key covers the version sold, or its first activation's, and a subscription or trial key covers every version.
email

The customer's email.

deviceLimit

Overrides the model's device limit: a whole number.

Range 1 to 1,000

seatReleaseDays

Overrides the model's Free unused seats: the days a device may go without checking in before its seat is freed, or null for never (see Free unused seats).

Range up to 365, never fewer than the key's Offline days

expiresAt

Overrides the model's expiry, in Unix milliseconds. Under a timed model it replaces the term, a term counted from first use included (see Timed models).

sendEmail

true to email the key (see Notes). false by default.

idempotencyKey

The same as the Idempotency-Key header, which takes precedence when both are sent.

Length 1 to 255 characters

version

The version sold: "2.4.1", "v2", 2, "2.4.1-beta+5". Only the major version counts, and 0.x counts as 1.

minMajor, maxMajor

The key's own range, overriding the model's. Send both or neither: whole numbers, with maxMajor null for "and later".

Range 1 to 99,999

upgradeMonths

Overrides the model's upgrade protection, in months of 30 days each. Applies only to a range with a highest version.

Range 0 to 120

entitlements

Replaces the model's entire set. null or {} for none. Each must be defined on the product's Entitlements tab, with a value of its type (400 unknown-entitlement or 400 entitlement-type otherwise), but for one its model grants as it is. See the rules.

meters

Replaces what the model counts, the whole set by meter code: { "exports": { "uses": 1000, "resets": "never", "offline": null } }. {} counts nothing. See Metered uses.

  • At most 8, each with whole uses from 0 to 1,000,000,000.
  • resets is never (the default) or period, a meter that comes back each billing period, on a subscription only.
  • offline is the allowance each device may spend while KeyGrant cannot be reached, null for the default.
count

How many keys to issue as one sale, each with the same terms. Without it a request issues one key, whatever the model's Keys per sale.

  • A key issued here records no amount (only a paid checkout does): its license page says none was recorded, and it never counts toward what a customer has paid.

Range 1 to 100

Response

  • 201 with { "key", "emailed" }.
  • With a count: { "key", "keys", "emailed" }, key the first and keys every key, first to last. sendEmail sends them in one email.
  • emailed is true only if the email was sent.
  • No license object: fetch it with GET /v1/licenses/:key.

Errors

404 unknown-product

No product of yours has that API ID.

409 product-disabled

The product is archived.

400 invalid-versions

A version, range or upgradeMonths that is invalid or inconsistent.

400 invalid-seat-release

A seatReleaseDays fewer than the key's Offline days.

409 idempotency-key-reused

The idempotency key was first used for another product, with a secret key of the other mode (live or test), or for another count.

402 plan-limit

Issuing this live license would take you past your plan's limit on active licenses, counted across all your tenants. A count is refused whole, never cut to what fits. The response includes dimension, limit, usage, plan and scope (owner: the usage is across all your tenants, under the plan that covers them).

402 test-limit

Your account already has 25 active test licenses, the most it can hold, or the keys a count asks for would take it past 25: a count is refused whole. The response includes limit and usage.

404 unknown-model

modelId is not a model of that product.

400 edition-upgrade-model, 400 device-upgrade-model, 400 uses-upgrade-model

modelId is an upgrade offer (an edition, devices or uses), which changes a key the customer already holds and issues none.

400 unknown-meter

A code in meters is not one of the product's meters. The response names it as meter.

400 meter-license-file, 400 meter-period-subscription-only, 400 meter-upgrade-offer

The key cannot carry its meters:

  • meter-license-file: it is sent as a license file, which never checks in.
  • meter-period-subscription-only: a meter comes back each billing period on a key that is not a subscription's.
  • meter-upgrade-offer: the model is an upgrade offer, which issues no key.
400 term-required, 400 term-ended

The key would have no term, or would run out before it was made (see Timed models):

  • term-required: a bare timed model with no expiresAt.
  • term-ended: a timed model whose end date has passed, with no expiresAt of its own.
400 email-required

A live key of a model that delivers files (a key file or a license file), or whose model locks Who can activate (see Who can activate), needs email: the file is made out to it, and the lock compares with it.

400 license-file-upgrade-protection

A live key's upgradeMonths above 0 under a model that delivers license files: a license file never checks in, so it cannot carry upgrade protection. A test key is issued as a typed key, and takes it.

500 mint-failed

The key could not be created. Retry with the same idempotency key.

Notes

  • sendEmail emails the key with the product's delivery template only when the key has an email, the product's License delivery tab is set to KeyGrant sends them, and its Key delivery email is on (see Key delivery email).
  • A live sale that names a version moves the product's current version up to it.

A retry with the same idempotency key is safe:

  • For the same product and with a secret key of the same mode, it returns 200 with the original license key and "emailed": false (and, for a count, every key it issued as keys). Nothing else happens.
  • Its other fields are not compared, except count: a retry naming another count returns 409 idempotency-key-reused.
  • Once the product is archived, a retry returns 409 product-disabled.
  • Idempotency keys are kept for the life of their license.

GET /v1/licenses/:key

Returns one license.

Response

200 with { license } (see The license object).

Errors

Only 404 unknown-license.

GET /v1/licenses

Lists your licenses, newest first.

Request

Query parameters, every one optional. One with an empty value is treated as absent.

product

A product's API ID.

email

The customer's full address, case-insensitive. Partial addresses do not match.

status

active (every currently valid key, including trial keys), trial, expired, revoked, or all (the default).

limit

How many licenses a page holds.

Range 1 to 100 ; Default 25

cursor

The nextCursor of the previous page.

Response

200
{  "licenses": [ { "key": "MYPROD-9K4T-2M8P-XQ7V-H3DW", "...": "..." } ],  "nextCursor": "1790899200000:MYPROD-9K4T-2M8P-XQ7V-H3DW"}
  • nextCursor is null on the last page; treat it as opaque.
  • Pages stay stable while new keys are created.

Errors

400 invalid-request

An invalid or unknown parameter. It is refused instead of being ignored, so a misspelled filter never returns every license.

404 unknown-product

The product is an API ID that is not one of your products.

GET /v1/checkouts/:session/license

Returns the license a Stripe checkout or a Paddle transaction created, or the license an upgrade checkout changed when the checkout named that key in its client_reference_id. After a checkout shows the page that polls it.

Request

No body. :session is the Checkout Session ID (cs_…, up to 255 characters) or the Paddle transaction ID (txn_…).

Response

  • 200 with { license }.
  • A checkout that created a sale of several keys returns its first key as license and every key, first to last, as licenses.

Errors

404 unknown-checkout

The license does not exist yet. Retry: the webhook that creates the key can land a moment after the buyer does.

404 checkout-upgraded

The checkout upgraded a key the buyer did not name, such as one found by an activation ID or the buyer's email address (see How KeyGrant finds the key).

404 checkout-held

KeyGrant held the sale: it could not place it on a key, so no key is coming (see Held sales).

404 checkout-refunded

A held sale was refunded since.

400 invalid-request

The ID is not a Checkout Session ID or a Paddle transaction ID.

POST /v1/licenses/:key/file

Returns the file of a key sent as a key file or a license file, for a backend that sends its own mail.

Request

The body is optional: { "resign": true } signs it again with the key that signs now, after a signing key leaked.

Response

200 with { file: { id, kind, kid, issuedAt, signedAt, filename, mediaType, content } }: save content under filename, or attach it to your own mail.

Errors

409 not-a-file-license

The key is a typed key, not sent as a file.

409 no-licensee

The key has no email address to make the file out to.

409 license-revoked

The key is revoked.

409 license-expires

The key is sent as a license file and has an end date.

Notes

The file is the one already made while the key says the same, and is signed again when the key changed.

POST /v1/licenses/:key/extend

Moves the key's expiry later.

Request

{ "expiresAt": <Unix milliseconds> }, the new expiry.

Response

200 with the updated { license }.

Errors

409 license-revoked

The key is revoked, and extending does not reinstate it. That includes a subscription key revoked after its grace period, and a trial key revoked after its expiry.

409 not-expiring

The key has no expiry to move: a perpetual key issued without one, a key issued with only a model type and no expiry, or a Stripe subscription whose first period Stripe has not reported yet.

409 term-not-started

A timed key still waiting for its first activation: its term has not started, so it has no end yet to move (see Timed models).

400 invalid-request

expiresAt is not a whole number, is already past, or is earlier than the key's current expiry.

409 test-ceiling-reached

A test license already past its 30-day limit.

402 plan-limit, 402 test-limit

The key had expired, and bringing it back would pass your plan's limit, counted across all your tenants (or this tenant's test license limit).

Notes

A test license cannot be extended past 30 days after it was created.

POST /v1/licenses/:key/versions

Sets which major versions the key covers, and its upgrade protection.

Request

{ "minMajor", "maxMajor", "upgradeUntil" }:

minMajor, maxMajor

Required, both, every time: whole numbers, with maxMajor null for "and later". A range may include a version you have not released yet.

Range 1 to 99,999

upgradeUntil

Optional. When upgrade protection ends, in Unix milliseconds, or null to end it now.

Response

200 with { license }.

Errors

400 invalid-versions

A missing bound, or a highest version below the lowest.

400 license-file-upgrade-protection

An upgradeUntil that opens or lengthens a window on a live key sent as a license file, which never checks in. Ending or shortening one it holds is taken.

POST /v1/licenses/:key/devices

Sets how many devices the key can be active on.

Request

{ "deviceLimit": 5 }:

deviceLimit

Required. How many devices the key can be active on, a whole number. It replaces the limit the key has, whether that came from its license model or from the deviceLimit it was issued with.

Range 1 to 1,000

Response

200 with { license }. Its activeDevices says how many devices are active now.

Errors

400 invalid-request

A missing limit, or one that is not a whole number from 1 to 1,000.

Notes

Lowering the limit below the devices the key is active on releases none of them. They keep working, and a new device's activation is refused with limit until fewer devices than the limit are active.

POST /v1/licenses/:key/seat-release

Sets when the key frees the seat of a device that stopped checking in (see Free unused seats).

Request

{ "seatReleaseDays": 30 }:

seatReleaseDays

Required. How many days a device of the key may go without checking in before its seat is freed, a whole number, or null to free none. It replaces what the key has, whether that came from its license model or from the seatReleaseDays it was issued with.

Range 1 to 365, never fewer than the key's Offline days

Response

200 with { license }.

Errors

400 invalid-request

A missing value, or one that is not a whole number from 1 to 365.

400 invalid-seat-release

Fewer days than the key's Offline days.

Notes

Turning it on, or shortening it, frees a device already unseen that long the next night.

POST /v1/licenses/:key/user-lock

Sets who may activate a new device on the key (see Who can activate).

Request

{ "userLock": "email" }:

userLock

Required. Who may activate a new device on the key. It replaces what the key has, whether that came from its license model or an earlier change.

  • off: anyone with the key.
  • email: whoever types the email it was sold to.
  • code: whoever types that email and a code emailed to it.

Response

200 with { license }, whose userLock is the new value.

Errors

400 invalid-request

A missing or unknown value.

400 email-required

The key has no email, and a lock compares with it.

400 user-lock-license-file

The key is sent as a license file.

400 user-lock-test-key

The key is a test license.

Notes

  • off is always taken.
  • A device that already holds a seat is never asked, so the change affects only devices activated afterwards.

Meters: /v1/licenses/:key/meters

Five routes for a key whose license model counts uses (see Metered uses). What they share:

  • Each responds 200 with { license }, but a spend.
  • Every route but the first answers 404 unknown-meter when the key does not hold the meter.
  • No change sends a webhook. A spend that takes a meter's last use sends meter.used_up.

POST …/:key/meters/:meter

Sets the meter's settings on the key, and adds the meter when the key lacks it and the product has it.

Request

{ uses?, resets?, offline? }, offline null for the default. A new meter needs its uses.

Errors

400 unknown-meter

The product has no meter of that code.

400 meter-period-subscription-only

resets is period on a key that is not a subscription's.

400 meter-license-file

The key is sent as a license file.

400 invalid-request

A body that does not fit, or a new meter without uses.

409 meter-cap

The key already holds 8 meters.

DELETE …/:key/meters/:meter

Takes the meter off the key. The app's use() of it is then no-meter; its spends stay in the key's history. No body.

POST …/:key/meters/:meter/add

Adds uses on top. They carry across billing periods until spent. An Idempotency-Key header is required: a replay adds nothing and answers the license as it stands.

Request

{ "uses": 500 }, 1 to 1,000,000,000.

Errors

400 idempotency-key-required

No Idempotency-Key header.

409 idempotency-key-reused

The same idempotency key with another body.

400 over-cap

The meter would pass 1,000,000,000.

POST …/:key/meters/:meter/reset

Starts a new period by hand. An Idempotency-Key header is required, as for an add, so a replay resets nothing twice. No body.

Errors

409 meter-not-periodic: the meter does not come back each billing period. Also 400 idempotency-key-required without the header.

POST …/:key/meters/:meter/use

A spend made by your backend, such as a cloud render the key pays for. Exact and idempotent by id, as an app's spend is. Your secret key is the proof, so no seat is asked.

Request

{ n?, id }: n 1 by default, and id 16 to 64 base64url characters, unique for the key.

Response

200 with { meter }, the meter as the license object shows it.

Errors

409 used-up

Fewer uses left than asked. The response includes left.

409 license-revoked, 409 license-expired

The key is revoked or has expired.

POST /v1/licenses/:key/revoke

Revokes the key.

Request

reason

Optional. refund, chargeback, subscription_canceled or manual (the default).

Response

200 with { "ok": true }.

Errors

Only 404 unknown-license.

Notes

  • The key is refused at the app's next check-in, and its devices keep their seats (see When a revoke takes effect).
  • Revoking a key that is already revoked also returns 200, records this call's time and reason, and sends license.revoked again, except a key a refund or a lost dispute revoked, which keeps that revoke: the call changes, records and sends nothing.
  • The API cannot reinstate a key.

GET /v1/licenses/:key/activations

Lists the devices the key is active on.

Response

200 with { "key", "activations": [ ... ] }, the key as stored and each device with:

id

The activation id, which DELETE frees.

fingerprint

The device's fingerprint, as device.activated carries it.

name, platform, appVersion

What the app's activation sent: unchecked text, null when it sent none.

lastSeen

Its last check-in, or null if it never checked in.

createdAt

When it activated.

releaseAt

The time after which the first nightly pass frees the seat, unless the device checks in first.

  • null while the key does not free unused seats.
  • null for a seat that never checked in.
  • null for a device-limit test license's placeholder seat.
  • The most recently seen device comes first; a device never seen comes by when it activated.
  • Every time is in Unix milliseconds.
  • A key on no device returns an empty list.

Errors

Only 404 unknown-license.

DELETE /v1/licenses/:key/activations/:id

Frees one device's seat.

Request

No body. :id is the activation id (GET …/activations lists the ids).

Response

200 with { "released": 1 }, or { "released": 0 } when :id is not a seat on that key.

Errors

Only 404 unknown-license.

POST /v1/products/:slug/releases

Reports a release: moves the product's current version up to its major, never down (see Your product's current version).

Request

{ "version" }:

version

Required. In the same format as version on POST /v1/licenses.

Response

200 with { "product", "currentMajor" }, the current version after the call.

Errors

400 invalid-versions

The version cannot be parsed.

404 unknown-product

A test secret key, or a product in another account.

GET /v1/products

Lists every product of your account, oldest first, each with its license models: the API IDs and the modelId an issue or a test license needs, for a build script that holds a secret key.

Request

No body and no parameters. It is not paged: it returns every product of the account.

Response

200 with { products }:

200
{  "products": [    {      "slug": "my-product",      "name": "My Product",      "keyPrefix": "MYPROD",      "currentMajor": 2,      "trialDays": 14,      "trialEntitlements": {},      "archived": false,      "createdAt": 1790899200000,      "meters": [ { "code": "exports", "name": "Exports", "description": null } ],      "models": [        {          "id": "mod_5c1e0a7b9d2f4e8a6b3c1d0e9f8a7b6c",          "name": "Perpetual",          "type": "perpetual",          "priceCents": 7900,          "deviceLimit": 3,          "delivery": "key",          "userLock": "off",          "versions": "at_purchase",          "meters": [ { "code": "exports", "uses": 1000, "resets": "never", "offline": null } ],          "entitlements": {},          "...": "..."        }      ]    }  ]}

Each product has:

slug

The product's API ID: what the SDKs, the license routes and /pubkey name it by.

name

The product's name.

keyPrefix

Its license keys' prefix: MYPROD of MYPROD-9K4T-2M8P-XQ7V-H3DW.

currentMajor

The highest major version it ships, which caps what upgrade protection keeps. A release moves it up.

trialDays

A device trial's length in days.

trialEntitlements

What a device trial's leases grant.

archived

true for an archived product, which issues no keys (409 product-disabled). Its keys keep working.

createdAt

When it was created, in Unix milliseconds.

meters

What its apps count: a list of { code, name, description }, code being what use() passes and description text or null. See Metered uses.

models

Its license models, in the order the dashboard's Models tab lists them.

The license models

Each entry of models holds the terms of one model, as the dashboard's Models tab sets them (see License models):

id

The model's ID: mod_ and 32 hex characters. It is what modelId takes on issue and on a test license.

name

The model's name.

type

perpetual, subscription, trial or timed.

description

Text, or null.

archived

true for an archived model, which sells nothing new. Its keys keep their terms.

priceCents, currency

The price in minor units (null for a free or trial model) and its currency.

interval

monthly or yearly for a subscription, null otherwise.

deviceLimit

How many devices a key sold under it can be active on.

graceDays

Days a key keeps working after it expires, for a subscription model; null is none.

trialDays

A trial model's length in days, null otherwise.

leaseDays

Offline days: each lease's length, 1 to 90, or null for the type's default.

seatReleaseDays

Free unused seats: the days a device may go without a check-in before its seat is freed, never fewer than its Offline days; null is off. See Free unused seats.

keysPerSale

How many keys one sale of it makes.

delivery

How its keys are delivered: key, key_file or license_file.

userLock

Who may activate a new device on its keys: off, email or code. See Who can activate.

versions

Which majors its keys cover: at_purchase (the version sold only), from_purchase (that and every later one), all or range.

minMajor, maxMajor

A range model's lowest major and its highest, maxMajor null for "and later".

upgradeMonths

Months of upgrade protection a key gets, 0 to 120.

upgradesExisting

true for an upgrade offer: buying it changes a key the buyer holds rather than issuing one.

upgradeFrom, upgradeTo

An edition upgrade's entitlements: the set a key must hold, and the set that replaces it. {} on any other model.

addsDevices

A device upgrade's devices per unit. 0 on any other model.

addsUses

A uses upgrade's uses per unit, by meter code. {} on any other model. See Sell more uses.

meters

What its keys count: a list of { code, uses, resets, offline }, each picked from the product's meters, its fields as an issue's meters takes them.

entitlements

What its keys grant.

termLength, termUnit, termStarts, endsAt

A timed model's term: termLength in termUnit (days or months), counted from termStarts (purchase or activation); or its fixed end date, endsAt. All null on any other type.

createdAt

When it was created, in Unix milliseconds.

Notes

  • A live key and a test key may both call it: a product and its terms are no license, so a test key reaches nothing a live customer holds through it.
  • It holds no key material: a product's public key is GET /pubkey.

POST /v1/test-licenses

Makes a test license in one of the states an app must handle, as the dashboard's Create test license does (see Testing your integration).

  • It is signed with the product's real key and carries test: true.
  • It lasts 30 days at most and is never emailed.
  • It counts toward the account's 25 active test licenses, never toward its plan.
  • Its history says it came from the API.

Request

Example
{  "product": "my-product",  "scenario": "device-limit"}
product

Required. The product's API ID.

scenario

Required. The state the license is made in: one of the six scenarios.

modelId

The license model, by its ID.

  • Without it: the product's oldest model that is not archived and issues keys (never an upgrade offer; for used-up, the oldest that counts uses), or, if it has none, a perpetual key sold under no model.
  • trial-ending ignores modelId and uses the product's trial model, or, if it has none, a trial key sold under no model.
version

The version it is for, in the same format as version on issue. Without it, a key that needs a version takes its first activation's.

idempotencyKey

The same as the Idempotency-Key header, which takes precedence when both are sent.

Length 1 to 255 characters

Scenarios

valid

A valid key.

trial-ending

A trial that ends in 24 hours.

expired

Expired an hour ago.

revoked

Revoked when it is created.

device-limit

One device allowed and already taken by a placeholder, so activating is refused.

used-up

Its first meter has no uses left. It needs a model that counts uses.

Response

201 with { key, scenario, license }, the license as GET /v1/licenses/:key shows it (see The license object):

201
{  "key": "MYPROD-9K4T-2M8P-XQ7V-H3DW",  "scenario": "device-limit",  "license": { "key": "MYPROD-9K4T-2M8P-XQ7V-H3DW", "...": "..." }}

Errors

404 unknown-product

No product of yours has that API ID.

404 unknown-model

modelId is not a model of that product.

409 product-disabled

The product is archived.

400 invalid-request

The body does not fit: a missing product, or a scenario that is not one of the six.

400 invalid-versions

A version that cannot be read.

400 edition-upgrade-model, 400 device-upgrade-model, 400 uses-upgrade-model

modelId is an upgrade offer, which changes a key the customer already holds and issues none.

400 term-ended

modelId is a timed model whose end date has passed (the expired scenario sets its own expiry, so it is never refused for this).

400 no-meters

The used-up scenario, with a model that counts nothing.

409 idempotency-key-reused

The idempotency key was first used for another product, for a live license, for another scenario, or for an issue of several keys.

402 test-limit

Your account already has 25 active test licenses, the most it can hold. The response includes limit and usage. An expired or revoked test license takes none of the 25.

429 rate-limited

Past the key's 600 requests a minute for issuing (shared with POST /v1/licenses), or the account's 200 test licenses a day (see Rate limits). The response includes retryAfter.

503 unavailable

The key's request could not be counted against its limit, so nothing was made. Retry after retryAfter seconds.

500 mint-failed

The key could not be created. Retry with the same idempotency key.

Notes

  • A request with the same idempotency key as an earlier one returns 200 with the key first made, and nothing else happens.
  • A live key and a test key may both call it, and a test key reaches the license afterwards, as it can reach any test license.
  • The 200 test licenses a day count from midnight UTC, and a request that replays an idempotency key spends none of them.