Quickstart
This quickstart walks through the full journey with fictitious sample data. You need a Seller account with at least one registered product, a Sandbox API key and its base URL, all issued during Seller onboarding.
-
Configure your environment.
Terminal window export LICENCHAIN_API_BASE_URL="https://sandbox-api.licenchain.example" # use the URL issued to youexport LICENCHAIN_API_KEY="..." # from your secret manager, never committed -
Submit a licensing request once your own order process has approved the order.
Terminal window curl --request POST "$LICENCHAIN_API_BASE_URL/api/integrations/v1/licensing-requests" \--header "Authorization: Bearer $LICENCHAIN_API_KEY" \--header "Content-Type: application/json" \--header "Idempotency-Key: 5d1f8a3c-2b7e-4c49-9f0a-6e3b2d4c8a17" \--data '{"externalOrderId": "SELLER-PO-EXAMPLE-001","productRef": "ai-specialist-standard","buyer": {"organizationName": "Example Research Cooperative","contactEmail": "licensing@example.com"},"price": {"currency": "EUR","netLicensedAmount": "1200.00"},"validity": {"validFrom": "2027-01-01T00:00:00Z","validUntil": "2028-01-01T00:00:00Z"},"quantity": 25}'// Server-side only. Never ship the API key to browsers, apps or agent prompts.const baseUrl = process.env.LICENCHAIN_API_BASE_URL;const apiKey = process.env.LICENCHAIN_API_KEY;const payload = {"externalOrderId": "SELLER-PO-EXAMPLE-001","productRef": "ai-specialist-standard","buyer": {"organizationName": "Example Research Cooperative","contactEmail": "licensing@example.com"},"price": {"currency": "EUR","netLicensedAmount": "1200.00"},"validity": {"validFrom": "2027-01-01T00:00:00Z","validUntil": "2028-01-01T00:00:00Z"},"quantity": 25};// Persist one idempotency key per order and reuse it on every retry of that order.const idempotencyKey = '5d1f8a3c-2b7e-4c49-9f0a-6e3b2d4c8a17';const response = await fetch(`${baseUrl}/api/integrations/v1/licensing-requests`, {method: 'POST',headers: {Authorization: `Bearer ${apiKey}`,'Content-Type': 'application/json','Idempotency-Key': idempotencyKey,},body: JSON.stringify(payload),});if (response.status === 201 || response.status === 200) {const result: { requestId: string; onboarding: { url: string; expiresAt: string } } = await response.json();// Store result.requestId with your order. Send result.onboarding.url only to the Buyer contact.// Do not log the onboarding URL: it is a single-use secret.} else {const problem: { code: string; traceId?: string } = await response.json();throw new Error(`Licensing request failed: ${problem.code} (trace ${problem.traceId ?? 'n/a'})`);}# Server-side only. Never ship the API key to browsers, apps or agent prompts.import osimport requestsbase_url = os.environ["LICENCHAIN_API_BASE_URL"]api_key = os.environ["LICENCHAIN_API_KEY"]payload = {"externalOrderId": "SELLER-PO-EXAMPLE-001","productRef": "ai-specialist-standard","buyer": {"organizationName": "Example Research Cooperative","contactEmail": "licensing@example.com",},"price": {"currency": "EUR","netLicensedAmount": "1200.00",},"validity": {"validFrom": "2027-01-01T00:00:00Z","validUntil": "2028-01-01T00:00:00Z",},"quantity": 25,}# Persist one idempotency key per order and reuse it on every retry of that order.idempotency_key = "5d1f8a3c-2b7e-4c49-9f0a-6e3b2d4c8a17"response = requests.post(f"{base_url}/api/integrations/v1/licensing-requests",headers={"Authorization": f"Bearer {api_key}","Idempotency-Key": idempotency_key,},json=payload,timeout=10,)if response.status_code in (200, 201):result = response.json()# Store result["requestId"]. Send result["onboarding"]["url"] only to the Buyer contact.# Do not log the onboarding URL: it is a single-use secret.else:problem = response.json()raise RuntimeError(f"Licensing request failed: {problem['code']} (trace {problem.get('traceId')})")A
201 Createdresponse contains the request ID and the Buyer onboarding link:{"schemaVersion": "1.0","requestId": "lreq_7Q2M9K4X8P3D","externalOrderId": "SELLER-PO-EXAMPLE-001","productRef": "ai-specialist-standard","termsVersion": "2026.2","status": "awaiting_buyer","onboarding": {"url": "https://sandbox.licenchain.example/onboarding/REDACTED-ONE-TIME-TOKEN","expiresAt": "2026-12-08T09:00:00Z"},"createdAt": "2026-11-08T09:00:00Z"} -
Send
onboarding.urlto the Buyer contact through your own channel. The link is single-use and must not be logged. -
Receive webhooks.
onboarding.completedtells you the Buyer accepted the terms; it is not issuance.licence.issueddelivers the authoritativelicenceKey— store it with the order. See Webhooks. -
Verify before granting new access.
Terminal window curl --request POST "$LICENCHAIN_API_BASE_URL/api/integrations/v1/licenses/verify" \--header "Authorization: Bearer $LICENCHAIN_API_KEY" \--header "Content-Type: application/json" \--data '{"licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42"}'// Server-side only. Returns true only when LicenChain reports valid === true.// Every other outcome (non-200, timeout, malformed body, valid !== true) denies new access.const baseUrl = process.env.LICENCHAIN_API_BASE_URL;const apiKey = process.env.LICENCHAIN_API_KEY;export async function isLicenceValid(licenceKey: string): Promise<boolean> {const payload = { licenceKey };try {const response = await fetch(`${baseUrl}/api/integrations/v1/licenses/verify`, {method: 'POST',headers: {Authorization: `Bearer ${apiKey}`,'Content-Type': 'application/json',},body: JSON.stringify(payload),signal: AbortSignal.timeout(5000),});if (response.status !== 200) return false;const result: { valid?: unknown } = await response.json();return result.valid === true;} catch {return false;}}// Example: the licenceKey you stored from the licence.issued event.const allowed = await isLicenceValid('11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42');# Server-side only. Returns True only when LicenChain reports valid is True.# Every other outcome (non-200, timeout, malformed body, valid is not True) denies new access.import osimport requestsbase_url = os.environ["LICENCHAIN_API_BASE_URL"]api_key = os.environ["LICENCHAIN_API_KEY"]def is_licence_valid(licence_key: str) -> bool:payload = {"licenceKey": licence_key}try:response = requests.post(f"{base_url}/api/integrations/v1/licenses/verify",headers={"Authorization": f"Bearer {api_key}"},json=payload,timeout=5,)if response.status_code != 200:return Falsereturn response.json().get("valid") is Trueexcept (requests.RequestException, ValueError):return False# Example: the licenceKey you stored from the licence.issued event.allowed = is_licence_valid("11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42")200 OK — illustrative valid result {"schemaVersion": "1.0","licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42","productRef": "ai-specialist-standard","buyerOrganizationId": "org_2B5D8F1H4K7M","licenceHolderId": "org_2B5D8F1H4K7M","seatLimit": 25,"issued": true,"valid": true,"status": "active","reason": null,"validFrom": "2027-01-01T00:00:00Z","validUntil": "2028-01-01T00:00:00Z","checkedAt": "2027-03-15T10:30:00Z","revision": 3}Grant access only when
validistrueand your own user authentication and assignment checks pass.
Next steps
Section titled “Next steps”- Understand every field and error in Submit licensing request and Verify license.
- Plan retries with Errors & retries.
- Building an MCP server or agent tool? Read MCP & agent access control.