Licenses (add-on)
Issuing, editing, suspending, revoking and regenerating license keys, and freeing a seat.
Provided by the KahunaCart Licenses plugin. One permission covers the whole admin API, because a licence key is the credential itself and reading the list is already handing out the goods. See Licenses.
List licenses
/api/v1/kahunacart/licenses
Search issued license keys, newest first, each with its status, its project name, the seats in use against the seats allowed, its product, its order, its customer and its two dates. q matches a fragment of a key, the project name the customer gave it, a buyer's email or an order number, which are the four things a support conversation ever starts with. Status expired is derived from the expiry date every time it is asked and is never stored, so an expired key appears under expired and not under active. Use get_license for one key with its activations and the delivery copy the customer was sent.
Parameters
| Name | Type | Description |
|---|---|---|
| q optional | string | Substring match on the key, its project name, the customer's email or the order number |
| status optional | string |
Anything else is ignored rather than refused
One of:
active, suspended, revoked, expired |
| product_id optional | integer | Only keys for this catalog product |
| variant_id optional | integer | Only keys for this variant |
| customer_id optional | integer | Only keys belonging to this customer |
| order_id optional | integer | Only keys minted by this order |
| page optional | integer |
At least 1.
Default:
1 |
| per_page optional | integer |
Clamped to 200, and the clamped value comes back in the response
Default:
25 |
| sort optional | string |
A column to sort every matching key by before the page is cut; leave out for newest first
One of:
key, project, product, status, seats, customer, order, expires |
| dir optional | string |
One of:
asc, descDefault:
asc |
Response
Answers {licenses, total, page, per_page, statuses}.
Get a license
/api/v1/kahunacart/licenses/{id}
One key with everything it knows about itself: the row and its effective status, every machine it has been activated on including the seats already freed, the product, variant, customer and order behind it, the licensing rule that governs it, every add-on that has been bought onto it, and a preview of the delivery copy and license files the customer receives. Call this before suspending, revoking or freeing a seat, because the activation ids free_license_seat needs are only here.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id, not the key itself |
Issue a license by hand
/api/v1/kahunacart/licenses
Mint a key with no order behind it, for a comp, a review copy or a replacement for somebody who bought through another channel, and return it the way get_license does. No order is invented to hang it on, so nothing lands in the store's revenue reports. A product is required and an unknown product, an unknown customer or a variant belonging to a different product answers 422. Name the owner by customer_id or by email; an email that already has an account gets the key on that account and a new one gets an account. seats, term_days and updates_days fall back to the store defaults when omitted, and sending any of them as null means unlimited seats, a perpetual key or updates forever. The key is drawn in the product rule's own layout and carries its prefix, so a comp key looks like the product's other keys, and the same delivery email an order-issued key triggers goes out.
Parameters
| Name | Type | Description |
|---|---|---|
| product_id required | integer | Catalog product the key is for |
| variant_id optional | integer | Must belong to product_id; picks the variant's own licensing rule |
| customer_id optional | integer | Existing customer to give the key to |
| email optional | string | Used only when customer_id is absent. An address with an account gets the key on it; a new address gets an account |
| seats optional | integer or null | Machines that may hold the key at once. null is unlimited, omitted is the store default |
| term_days optional | integer or null | Days until the key expires. null is perpetual, omitted is the store default |
| updates_days optional | integer or null | Days of updates entitlement, which is separate from the term. null is updates forever, omitted is the store default |
| meta optional | object | Free-form object handed back to the customer's software on every validation |
| note optional | string | Private note, never shown to the customer |
Edit a license
/api/v1/kahunacart/licenses/{id}
Change what a merchant is allowed to change on an issued key: the project name, the seat allowance, the expiry, the updates window, the private note, the metadata and who it belongs to. Only the fields sent are touched, and a null on seats, expires_at or updates_until clears the limit rather than zeroing it. Lowering seats below the machines already holding one is refused with 409 seats_below_live, and the whole request is left unapplied when that happens, so a rejected seat change never half-saves the expiry beside it. Pushing the two dates out is how an operator extends a key by hand; a customer buying more time does it through a renewal product instead, which extends from max(now, current updates_until) and writes the extension on the order.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
| label optional | string | The customer's own name for this key — the project or site it runs on. The customer edits it from their account too. An empty string clears it |
| seats optional | integer or null | null is unlimited. Refused when lower than the live activations |
| expires_at optional | integer or null | Unix timestamp the key expires on. null is perpetual |
| updates_until optional | integer or null | Unix timestamp the updates window closes on. null is forever. A closed window does not stop the key validating |
| customer_id optional | integer or null | Move the key to another customer. null detaches it from every account |
| meta optional | object | Replaces the whole metadata object. null or an empty object clears it |
| note optional | string | Private note, never shown to the customer. Empty clears it |
Suspend a license
/api/v1/kahunacart/licenses/{id}/suspend
Switch a key off for now, which is the one to use for a chargeback under investigation or a customer in arrears. Validation starts answering valid: false with status suspended, and the activations are left in place on purpose so reinstate_license puts the customer back exactly where they were rather than making them reactivate twelve machines by hand. Suspending an already suspended key is a no-op success; a revoked key answers 409 invalid_transition, because revocation is permanent. Returns the same payload get_license does.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
| note optional | string | Private note recorded against the key, never shown to the customer |
Reinstate a license
/api/v1/kahunacart/licenses/{id}/reinstate
Undo a suspension and put the key back to active with its activations untouched. Reinstating an already active key is a no-op success. A revoked key cannot be reinstated and answers 409 invalid_transition: revocation exists precisely so a key can stop working permanently, and issue_license or regenerate_license is the way to give the customer a working key again. Returns the same payload get_license does.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
Revoke a license
/api/v1/kahunacart/licenses/{id}/revoke
Kill a key permanently and free every seat it holds. There is no way back from this: a revoked key cannot be suspended, reinstated or renewed, it has no license files, and the only remedy for a mistake is issuing a new key. A reason is required and is kept on the record forever, because "revoked, nobody remembers why" is a support ticket waiting to happen. Revoking an already revoked key answers 409 revoked. Prefer suspend_license for anything reversible. Returns the same payload get_license does.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
| reason required | string | Why the key is being killed. Required, kept on the record, and an empty one answers 422 |
Regenerate a license key
/api/v1/kahunacart/licenses/{id}/regenerate
Replace a leaked key. The current key is revoked rather than deleted, so software still holding it is told plainly that it is dead, and a fresh key is minted carrying the same order, customer, product, variant, seats and dates, drawn in the product rule's own layout. This is destructive on the old key and cannot be undone. The response is the new key's get_license payload with the revoked one under previous.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
Free an activation seat
/api/v1/kahunacart/licenses/{id}/activations/{activationId}
Release one seat on the customer's behalf, which is the support action for a machine that died still holding one. The activation row is kept and marked deactivated rather than deleted, so the history of which machines the key has been on stays intact. Get the activation id from get_license; an id belonging to another key answers 404. Returns whether a seat was actually freed, the new seat counts and the full activation list.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | License id |
| activationId required | integer | Activation id, from get_license |
A refusal from the licensing service carries a stable machine code in the body and in an X-KahunaCart-Error header.