Skip to Content

Errors

Every error of the API has the same format: an HTTP status, and an error object with a code and a message.

// 409 Conflict { "error": { "code": "key_taken", "message": "Another license of the product already uses this key" } }
  • code is stable: branch on it in your code.
  • message explains the error to a developer, in English. It can change at any time: don’t parse it, and don’t show it to your customers as is.

Each endpoint lists the codes it can return in the API reference.

Validation errors

An invalid path, query parameter or body answers 422, not 400, with the validation_error code and an issues array, giving the path and message of each invalid value:

// 422 Unprocessable Entity { "error": { "code": "validation_error", "message": "Invalid request", "issues": [ { "path": ["email"], "message": "Invalid email address" }, { "path": ["maxVersion", "lockMinorAt"], "message": "The minor version can't be locked before the major version" } ] } }

path lists the keys leading to the value, numbers for the positions in an array. Every invalid value is listed, not only the first one.

Fields an endpoint doesn’t know are ignored, not refused: a misspelled optional field doesn’t cause a validation error, it’s left out. See conventions.

Status codes

StatusMeansRetry?
200, 201, 204Success. 201 for creations, 204 when there’s nothing to return.
401Missing or invalid credentials.No: fix the credentials.
402Your plan’s limits.No: upgrade, or reduce your usage.
403The credentials can’t do this.No.
404Not found, or not reachable with these credentials.No.
409Conflicts with the current state, like a key already used.No: change the request, or the state first.
413, 415An uploaded file is too large, or of the wrong type.No.
422Invalid request.No: fix the request.
429Too many requests with this API key.Yes, after the Retry-After seconds.
500Unexpected error on our side, with internal_error.Yes, with a delay.
502A third party, like Stripe, couldn’t be reached.Yes, with a delay.

Common codes

These can be returned by any endpoint.

StatusCodeWhen
401unauthorizedNo API key, token or session.
401invalid_api_keyThe API key doesn’t exist, or was revoked.
401invalid_tokenThe token’s signature is invalid, it expired, or the resource it’s scoped to was deleted.
402plan_limit_exceededYour organization stayed over its plan’s limits after the grace period: the API is blocked until you upgrade or reduce your usage. See plans and billing.
403insufficient_roleFrom the app: your role doesn’t allow this. The message names the permission needed.
403token_not_allowedThe endpoint doesn’t accept tokens, or not of this type.
403insufficient_scopeThe token lacks the permission the endpoint needs.
404not_foundThe resource doesn’t exist, or the credentials don’t reach it: a live API key asking for a test resource, a token asking for another customer’s license.
422validation_errorInvalid request, with the issues.
429rate_limitedThe API key made more than 10 requests in the last second. Wait for the Retry-After header’s seconds. See rate limit.
500internal_errorUnexpected error.

404 doesn’t tell a resource that doesn’t exist from one you can’t reach, on purpose: nobody learns what exists in an organization they can’t access.

Codes by resource

Products

StatusCodeWhen
402plan_limit_reachedCreating a product would go over your plan’s products.
403api_key_modeCreating a test product with a live API key, or the opposite.
409slug_takenAnother product of the organization uses this slug.
409product_in_useDeleting a live product that still has licenses.
400not_test_productResetting a live product: only test products can be reset.
413logo_too_largeThe logo is 1 MB or more.
415unsupported_logo_typeThe logo isn’t a WebP, PNG, JPEG or SVG image.

Policies

StatusCodeWhen
409slug_takenAnother policy of the product uses this slug.
409default_requiredUnsetting the default policy: make another one the default instead.
409policy_in_useDeleting a policy that licenses follow.
409last_policyDeleting the last policy of a product.

Customers

StatusCodeWhen
409email_takenAnother customer of the product uses this email.
409customer_in_useDeleting a customer who still has licenses.
409test_recipient_not_memberEmailing a customer of a test product who isn’t a member of your organization. See emails in test mode.
409portal_disabledEmailing a portal link while the product’s portal is disabled.

Licenses

StatusCodeWhen
404policy_not_foundThe product has no policy with this policyId.
409key_takenAnother license of the product uses this key.
409license_in_useDeleting a license used on active machines: deactivate them first.
409test_recipient_not_memberSending keys of a test product to a customer who isn’t a member of your organization.

Validating a key has its own codes: see handling validation errors.

Releases

StatusCodeWhen
409version_takenThe product already has a release with this version.

Stripe integration

StatusCodeWhen
422invalid_api_keyThe Stripe key isn’t a secret or restricted key, or Stripe rejected it.
422key_mode_mismatchA test mode key for a live product, or the opposite.
422missing_permissionThe Stripe key lacks permissions, listed in the message. See permissions.
409webhook_url_unreachableStripe can’t reach LicenseManager’s webhook URL.
409not_connectedMapping a price while Stripe isn’t connected in the product’s mode.
409price_mappedThe price already creates licenses of a product.
404price_not_foundThe Stripe account has no price with this id.
422price_unusableThe price can’t create licenses, like a recurring price. The message tells why.
502provider_unavailableStripe couldn’t be reached.

Billing

These endpoints only work from the app.

StatusCodeWhen
403session_requiredCalled with an API key or a token.
403forbiddenYour role can’t manage the subscription.
400custom_planYour organization is on a custom plan: contact us to change it.
400already_on_planYour organization is already on this plan.

Handling errors in your code

  • Branch on code, never on message, and treat unknown codes like their status.
  • Retry 500 and 502 a few times, waiting longer each time, and network errors too. Retry 429 after the Retry-After seconds. Don’t retry other errors: they’ll fail the same way.
  • Creating twice: when a request creating a license times out, it may have been created anyway. List the customer’s licenses before retrying, or create it with your own key, so the retry fails with key_taken rather than creating a second license.