Licenzy API reference
Reference the Licenzy /v1 runtime API for checkout, access checks, entitlements, and usage consumption. Configure projects, API keys, Stripe setup, outbound Licenzy webhooks, and billing from the Licenzy portal.
Runtime API reference
Use these /v1 endpoints from server-side code. Try It examples are local previews only.
API key authenticated /v1 reference
Use these endpoints from server-side code. API keys select the account and mode, test mode remains available for setup, and live writes require an active or trial commercial subscription.
- Global API key auth: Send
Authorization: Bearer lz_test_...orlz_live_.... - Modes: Data is scoped by
account_idand API key mode. Runtime request bodies do not switch modes. - Common auth errors:
missing_bearer_token,invalid_api_key, andplan_mode_not_allowed.
Runtime checkout
/v1/checkout/sessionCreate checkout session
Creates or returns a Stripe Checkout Session for a configured product in the current project.
Auth: Required. Uses a Licenzy runtime API key from server-side code.
Required headers
AuthorizationrequiredBearer <api_key>Licenzy runtime API key. Each API key belongs to one project, so runtime requests are scoped to that project's products, entitlements, Stripe connection, and outbound webhook activity.
Content-Typerequiredapplication/jsonRequired for requests with JSON bodies.
Idempotency-KeyrequiredstringRequired for this write. Checkout keys are limited to 200 characters.
Body
subject_refrequiredstringCustomer or subject reference, min 1 and max 200 characters.
product_coderequiredstringConfigured product code, min 1 and max 100 characters.
Request examples
curl -X POST 'https://api.licenzy.app/v1/checkout/session' \
-H 'Authorization: Bearer lz_test_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: example-key-001' \
--data '{
"subject_ref": "customer_123",
"product_code": "PACK_1000"
}'Responses
{
"attempt_id": "uuid",
"status": "checkout_created",
"checkout_url": "https://checkout.stripe.com/c/pay/...",
"mode": "test"
}Errors
{
"error": "missing_idempotency_key"
}{
"error": "invalid_body",
"details": {}
}{
"error": "product_not_found"
}{
"error": "invalid_product_kind"
}{
"error": "idempotency_conflict",
"message": "The supplied Idempotency-Key was already used with different request parameters."
}{
"error": "stripe_connection_not_configured",
"code": "STRIPE_CONNECTION_NOT_CONFIGURED",
"message": "Stripe connection is not configured for this account and mode.",
"details": {
"account_id": "uuid",
"mode": "test"
}
}{
"error": "missing_bearer_token"
}{
"error": "invalid_api_key"
}{
"error": "plan_mode_not_allowed",
"code": "PLAN_MODE_NOT_ALLOWED",
"message": "Current plan does not allow this mode.",
"details": {
"mode": "live",
"allowed_modes": ["test"],
"plan_code": "free"
}
}Notes
- Use the Licenzy portal for API key creation, Stripe credential setup, product mapping, outbound Licenzy webhook configuration, and billing operations.
- Idempotency is required. An existing matching attempt returns the same Stripe session URL when available.
- Configure the Stripe inbound webhook in Stripe so Stripe can later send /v1/webhooks/stripe/tenant to finalize the purchase and update entitlements.
Runtime access
/v1/access/checkCheck access
Minimal boolean access check for a subject.
Auth: Required. Uses a Licenzy runtime API key from server-side code.
Required headers
AuthorizationrequiredBearer <api_key>Licenzy runtime API key. Each API key belongs to one project, so runtime requests are scoped to that project's products, entitlements, Stripe connection, and outbound webhook activity.
Content-Typerequiredapplication/jsonRequired for requests with JSON bodies.
Body
subject_refrequiredstringCustomer or subject reference, min 1 and max 200 characters.
Request examples
curl -X POST 'https://api.licenzy.app/v1/access/check' \
-H 'Authorization: Bearer lz_test_...' \
-H 'Content-Type: application/json' \
--data '{
"subject_ref": "customer_123"
}'Responses
{
"subject_ref": "customer_123",
"allowed": true
}Errors
{
"error": "invalid_body",
"details": {}
}{
"error": "missing_bearer_token"
}{
"error": "invalid_api_key"
}{
"error": "plan_mode_not_allowed",
"code": "PLAN_MODE_NOT_ALLOWED",
"message": "Current plan does not allow this mode.",
"details": {
"mode": "live",
"allowed_modes": ["test"],
"plan_code": "free"
}
}Notes
- In live mode, blocked commercial account state returns allowed: false.
- Checks subscription, then time pass, then usage pack. It does not consume usage.
- Response intentionally omits reason/details.
/v1/customer/access/:subject_refCustomer access detail
Detailed access status and entitlement availability for a subject.
Auth: Required. Uses a Licenzy runtime API key from server-side code.
Required headers
AuthorizationrequiredBearer <api_key>Licenzy runtime API key. Each API key belongs to one project, so runtime requests are scoped to that project's products, entitlements, Stripe connection, and outbound webhook activity.
Path params
subject_refrequiredstringCustomer or subject reference, min 1 and max 200 characters.
Request examples
curl -X GET 'https://api.licenzy.app/v1/customer/access/:subject_ref' \
-H 'Authorization: Bearer lz_test_...'Responses
{
"subject_ref": "customer_123",
"has_access": true,
"status": "active",
"entitlements": [
{
"kind": "usage_pack",
"status": "active",
"plan": null,
"expires_at": null,
"usage_remaining": 990,
"available": true,
"availability_reason": "usage_pack_available"
}
]
}Errors
{
"error": "invalid_subject_ref",
"details": {}
}{
"error": "missing_bearer_token"
}{
"error": "invalid_api_key"
}{
"error": "plan_mode_not_allowed",
"code": "PLAN_MODE_NOT_ALLOWED",
"message": "Current plan does not allow this mode.",
"details": {
"mode": "live",
"allowed_modes": ["test"],
"plan_code": "free"
}
}Notes
- Use this for post-payment polling, customer workspace inspection, or backend investigation.
- Does not consume usage.
- Availability reasons include subscription_active, subscription_inactive, time_pass_active, time_pass_expired, usage_pack_available, usage_pack_empty, usage_pack_inactive, and commercial_account_blocked.
- Human operator workflows such as denial investigation, history review, and manual recovery live in the Licenzy portal.
/v1/entitlements/:subject_refEntitlements
Raw entitlement rows for a subject plus computed status.
Auth: Required. Uses a Licenzy runtime API key from server-side code.
Required headers
AuthorizationrequiredBearer <api_key>Licenzy runtime API key. Each API key belongs to one project, so runtime requests are scoped to that project's products, entitlements, Stripe connection, and outbound webhook activity.
Path params
subject_refrequiredstringCustomer or subject reference.
Request examples
curl -X GET 'https://api.licenzy.app/v1/entitlements/:subject_ref' \
-H 'Authorization: Bearer lz_test_...'Responses
{
"subject_ref": "customer_123",
"mode": "test",
"now": "2026-05-07T10:00:00.000Z",
"entitlements": [
{
"id": "ent_123",
"account_id": "acc_123",
"mode": "test",
"subject_ref": "customer_123",
"kind": "usage_pack",
"plan": null,
"expires_at": null,
"usage_remaining": 990,
"status": "active",
"updated_at": "2026-05-07T10:00:00.000Z",
"effective_status": "active",
"is_active": true
}
]
}Errors
{
"error": "missing_bearer_token"
}{
"error": "invalid_api_key"
}{
"error": "plan_mode_not_allowed",
"code": "PLAN_MODE_NOT_ALLOWED",
"message": "Current plan does not allow this mode.",
"details": {
"mode": "live",
"allowed_modes": ["test"],
"plan_code": "free"
}
}Notes
- Read-only. Does not consume usage.
- Use the portal for product configuration, support/debug operations, or account administration. This endpoint is only for runtime state inspection.
Runtime usage
/v1/usage/consumeConsume usage
Atomically consumes units from a subject's usage_pack entitlement.
Auth: Required. Uses a Licenzy runtime API key from server-side code.
Required headers
AuthorizationrequiredBearer <api_key>Licenzy runtime API key. Each API key belongs to one project, so runtime requests are scoped to that project's products, entitlements, Stripe connection, and outbound webhook activity.
Content-Typerequiredapplication/jsonRequired for requests with JSON bodies.
Idempotency-KeyrequiredstringRequired for this write. Checkout keys are limited to 200 characters.
Body
subject_refrequiredstringCustomer or subject reference, min 1 and max 200 characters.
unitsrequiredintegerPositive unit count, max 1,000,000.
Request examples
curl -X POST 'https://api.licenzy.app/v1/usage/consume' \
-H 'Authorization: Bearer lz_test_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: example-key-001' \
--data '{
"subject_ref": "customer_123",
"units": 10
}'Responses
{
"ok": true,
"consumed": 10,
"usage_remaining": 990
}{
"ok": true,
"idempotent": true,
"consumed": 10,
"usage_remaining": 990
}Errors
{
"error": "missing_idempotency_key"
}{
"error": "invalid_body",
"details": {}
}{
"error": "insufficient_usage"
}{
"error": "idempotency_conflict"
}{
"error": "plan_limit_exceeded",
"code": "PLAN_LIMIT_MONTHLY_USAGE_EXCEEDED",
"message": "Plan limit exceeded for monthly usage.",
"details": {
"limit": 10000,
"current": 9990,
"requested": 20,
"plan_code": "pro",
"period_start_utc": "2026-05-01T00:00:00.000Z",
"period_end_utc": "2026-06-01T00:00:00.000Z"
}
}{
"error": "missing_bearer_token"
}{
"error": "invalid_api_key"
}{
"error": "plan_mode_not_allowed",
"code": "PLAN_MODE_NOT_ALLOWED",
"message": "Current plan does not allow this mode.",
"details": {
"mode": "live",
"allowed_modes": ["test"],
"plan_code": "free"
}
}Notes
- Idempotency is required and enforced through the usage ledger uniqueness constraint.
- Scoped by API key mode. Writes require commercial status active or trial.
- Request body is only subject_ref plus units. The meter is derived from the active usage_pack entitlement.
This reference intentionally excludes /portal/* cookie/session routes and Stripe inbound plus outbound operational webhook surfaces that are not part of the public tenant-facing runtime API. Documented endpoints: 5.
Outbound payload glossary
Outbound Licenzy payloads are source-aware and reason-aware. Nullable fields are expected when a field is not relevant to the current event.
- Common fields may include
subject_ref,project_id,mode,product_code,kind,status,reason,source,entitlement_id,attempt_id,units,usage_remaining,amount_total,currency,stripe_checkout_session_id,stripe_payment_intent_id,stripe_subscription_id,current_period_end,expires_at,canceled_at,actor_user_id,metadata,created_at, andupdated_at. statusdescribes the runtime entity state relevant to the current event, not the HTTP delivery status in the portal.sourcedescribes where the runtime state change originated: Stripe lifecycle processing, a portal operator action, or a runtime API operation.reasonis operational context for why the event exists. Treat it as descriptive context, not a closed enum or the only source of truth.attempt_ididentifies the checkout attempt when the event still has current checkout-attempt context.entitlement_ididentifies the runtime entitlement row involved in the change when one exists.amount_totalis the billing amount for the current event snapshot when billing context is relevant.metadatacarries the current event-specific metadata snapshot. It can differ across Stripe, portal, and runtime sources.current_period_end,expires_at, andcanceled_atare lifecycle timestamps that appear only when the event needs them.created_atandupdated_atbelong to the payload snapshot emitted by Licenzy. Treat portal delivery history timestamps separately from payload entity timestamps.source=stripeincludes Stripe and checkout-attempt context only when it is current and relevant.source=portalincludesactor_user_idfor manual operator events and does not backfill stale Stripe fields.source=runtimeincludes runtime usage context and does not backfill stale Stripe or portal fields.- Nullable fields are expected whenever the current event does not need billing, operator, or usage-specific context.
{
"subject_ref": "user_123",
"project_id": "proj_123",
"mode": "live",
"product_code": "PRO_MONTHLY",
"kind": "subscription",
"status": "active",
"reason": "<stripe-derived reason>",
"source": "stripe",
"entitlement_id": "ent_123",
"attempt_id": "att_123",
"amount_total": 7900,
"currency": "usd",
"stripe_checkout_session_id": "cs_test_123",
"stripe_payment_intent_id": "pi_123",
"stripe_subscription_id": "sub_123",
"current_period_end": "2026-06-01T00:00:00.000Z",
"expires_at": null,
"canceled_at": null,
"metadata": {},
"created_at": "2026-05-18T09:00:00.000Z",
"updated_at": "2026-05-18T09:00:00.000Z"
}{
"subject_ref": "user_123",
"project_id": "proj_123",
"mode": "live",
"product_code": "PACK_1000",
"kind": "usage_pack",
"status": "active",
"reason": "<portal operation reason>",
"source": "portal",
"entitlement_id": "ent_123",
"usage_remaining": 1000,
"actor_user_id": "usr_123",
"metadata": {},
"created_at": "2026-05-18T09:15:00.000Z",
"updated_at": "2026-05-18T09:15:00.000Z"
}{
"subject_ref": "user_123",
"project_id": "proj_123",
"mode": "live",
"product_code": "PACK_1000",
"kind": "usage_pack",
"status": "active",
"reason": "<runtime usage reason>",
"source": "runtime",
"entitlement_id": "ent_123",
"units": 10,
"usage_remaining": 990,
"metadata": {},
"created_at": "2026-05-18T09:20:00.000Z",
"updated_at": "2026-05-18T09:20:00.000Z"
}Error glossary
These are the most common runtime error codes you should handle explicitly in backend integrations:
missing_idempotency_key: checkout or usage write was sent without the requiredIdempotency-Keyheader.idempotency_conflict: the same idempotency key was reused with different request parameters.product_not_found: the suppliedproduct_codedoes not exist in the selected project and mode.invalid_product_kind: the target product cannot be used for the requested runtime flow.stripe_connection_not_configured: Stripe is not configured for the selected project and mode.plan_mode_not_allowed: the current Licenzy plan does not allow that mode, usuallylive.insufficient_usage: the subject does not have enough remaining usage units to fulfill the request.plan_limit_exceeded: the account hit a commercial or plan-level usage limit.
Common integration sequence
Most product integrations use the APIs in this order:
- 01Configure Stripe credentials, products, API keys, and any outbound Licenzy webhooks in the Licenzy portal.
- 02Make sure the API keys, products, Stripe setup, and outbound Licenzy webhooks you use all belong to the same project context.
- 03Start checkout from your backend with
POST /v1/checkout/session. - 04Redirect the user to the returned Stripe Checkout URL.
- 05Register the Stripe inbound webhook in Stripe so Stripe can call
/v1/webhooks/stripe/tenant. Your app should not call that inbound Stripe route directly. - 06Read access with
POST /v1/access/checkor inspect entitlements after webhook processing. - 07For usage packs, consume units with
POST /v1/usage/consumefrom your backend. - 08Use the Licenzy portal for support/debug operations, outbound webhook delivery inspection, and manual entitlement recovery.
The full integration example shows the same sequence as an end-to-end implementation.
Key guides
- Stripe setup for connecting credentials, storing the Stripe inbound webhook secret, and aligning products and modes before runtime calls.
- Access after Stripe payment for the post-checkout verification path.
- Webhook and event architecture for the platform mental model across inbound Stripe delivery and outbound Licenzy events.
- Outbound webhook delivery contract for receiver semantics, retries, deduplication, and acknowledgement patterns.
- Support and operational investigation for portal-side customer investigation and manual recovery workflows.
- Usage-based billing with Stripe for credit and usage-pack flows.