Integrate Licensr (for coding agents)
This page is meant to be handed to a coding agent (Claude Code, Cursor, Copilot and similar) together with the values the seller copies from their Licensr dashboard. Everything needed for a working integration is on this page. A plain-text index of the docs is at /llms.txt and the API spec at /openapi.json.
Inputs from the seller
The seller's dashboard gives you these values. Ask the seller for any that are missing; never invent them.
| Value | Example | Where it is used |
|---|---|---|
| API base URL | https://api.licensr.app | Every API call. Staging is https://api.staging.licensr.app. |
| Product slug | resizer-pro | plugin_slug on license calls; ?plugin= on the buy page link. |
| Store slug | acme | The hosted buy page URL. |
| Client API key | pk_live_... | License calls. Safe to ship inside the product. |
| Plan ID | 1c4f... (UUID) | Only for checkout from the seller's own site. Copy ID on a plan. |
| Full-access key | pk_live_... | Same prefix as a client key; the dashboard labels which is which. Only for checkout from the seller's own site. Server-side only. |
Use the pk_live_ key the seller gives you. A pk_test_ key is a separate pool for a dev or staging app: it cannot see a live license, its checkout sells the plan's Stripe test price, and a buy-page purchase is always live.
Never put a full-access key in code that ships to customers (a plugin, an app, a browser bundle, a public repository). Read it from an environment variable on the seller's server, for example LICENSR_SERVER_KEY. A Client key cannot start checkouts, so it is the only key that belongs in shipped code.
1. Let customers buy
Pick one. Both end the same way: after payment Licensr creates the license and emails the key to the buyer. You do not handle payment webhooks. If the seller sends license emails from their own server, the key arrives in a license.issued webhook instead and their server emails it.
Option A: link to the hosted buy page
Point the product's Buy link or button at:
https://licensr.app/buy/<store-slug>?plugin=<product-slug>
Nothing else is needed. Leave out ?plugin= to show every product in the store.
Option B: checkout from the seller's own site
Use this when the seller wants buyers to stay on their own website. Call Licensr from the seller's server, never from the browser, because it needs the full-access key:
curl -s -X POST "$LICENSR_API_BASE/v1/billing/checkout" \
-H "Authorization: Bearer $LICENSR_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "<plan-id>",
"customer_email": "buyer@example.com",
"success_url": "https://seller.example/thanks",
"cancel_url": "https://seller.example/pricing"
}'
The response is {"checkout_url": "..."}. Redirect the buyer's browser to it. After paying they return to success_url.
customer_emailis where the license key is sent. Ask the buyer for it on the seller's page.success_urlandcancel_urlare optional. Each must usehttps, and its website (https://host[:port], no path) must be listed on the product in the dashboard under Advanced options: Allowed websites for Browser products, Checkout return websites for Native ones. Otherwise the call fails with403 redirect_not_allowed.- A Client key gets
403 insufficient_scopehere. That is expected: use the full-access key.
If the seller only sells this way, they can turn the hosted buy page off under Storefront in the dashboard. Checkout from their own site keeps working.
2. Check the license in the product
Three calls, all with Authorization: Bearer <client key> and Content-Type: application/json.
Activate when the customer enters their key:
POST /v1/license/activate
{"license_key": "lic_...", "plugin_slug": "<product-slug>", "activation_type": "seat", "identifier": "<stable machine id>"}
Save the returned activation_id. activation_type is seat (one per device) or domain (one per website hostname), matching the product. Calling activate again with the same identifier is safe and uses no extra slot.
Validate right after activating, at every launch, and about once a day:
POST /v1/license/validate
{"license_key": "lic_...", "plugin_slug": "<product-slug>"}
Deactivate from a Deactivate button:
POST /v1/license/deactivate
{"license_key": "lic_...", "activation_id": "<saved activation_id>"}
Rules that matter
- Unlock only when validate returns
"valid": true. Stay locked on"valid": false, whatever the reason. The HTTP status is200even for a bad key. - If there is no
validfield (offline, rate limited, server error), keep the previous state. Do not lock a paying customer because the network failed. identifiermust be the same every time on one install and different on every machine: a hash of the OS machine id plus the product slug works. Do not use MAC addresses or the license key.- Browser products (code in a web page or extension) send an
Originheader automatically; native apps do not, which is fine for products set to Native.
Full field and error reference: License calls. SDKs that wrap these calls: JavaScript, C++, C# / Unity, Go.
3. Optional: manage licenses from the seller's server
With the full-access key, the seller's server can also list and look up licenses, buyers and subscriptions, issue a license for a sale made elsewhere, revoke one, replace its key, free a device or website, or erase a buyer: see the Seller API. To send license emails from the seller's own domain, or react to purchases, refunds and renewals, use webhooks.
4. Test it
- Ask the seller to issue a license under Customers → Issue license, with Live selected, so no payment is needed.
- Run activate, then validate, with that client key against
https://api.licensr.app. Expect"valid": true. - Deactivate and confirm validate still answers, and that the slot is free in the dashboard.
Reference
- License calls: every field and error
- In-app and own-site checkout
- Offline tokens for products that must work without a network
- API reference