Skip to content
Oclavex is in private preview, available to selected users. Public signup is blocked. Request access at [email protected].

Error codes

Every API failure returns an RFC 9457 application/problem+json body with two stable extensions: a numeric code and a dotted codeName.

{
"status": 403,
"title": "Forbidden by policy",
"detail": "Activation is blocked by the entitlement's activation policy.",
"code": 2011,
"codeName": "license.forbidden_by_policy"
}

Branch on code or codeName, never on detail or title — those are human-facing prose and may change; the codes are contract. New codes may be added over time (an additive, backward-compatible change), so treat unknown codes as a generic failure of the given HTTP status.

When an activation is refused because something it needed was missing, the problem also carries a requires member, so your application can act without reading the message:

requires What to do
sign_in Sign the user in and activate again.
verified_email Activate again with the user’s email address; they confirm it from the link they receive.
assigned_user The code was issued to another user. Sign in as that user, or ask your software provider for a code of your own.
organization_member The license is limited to one organization the signed-in user isn’t a member of.
verified_user Your software provider hasn’t verified this user yet.
hardware_key Activate with a YubiKey, through the hardware-key add-on.
hardware_key_touch Activate with a YubiKey set up to ask for a touch.

A caller with no signed-in user only ever sees sign_in or verified_email, with status 401, code license.authentication_required and a WWW-Authenticate header: Bearer when no token was sent, or Bearer error="insufficient_user_authentication" (RFC 9470) when the token has no user behind it. The other values go to a signed-in caller, with status 403. An unknown, expired or used activation code is refused before anything else, as before, and carries no requires.

Codes are globally unique and grouped in ranges: general platform errors are 1000–1999, licensing 2000–2999, account/directory 3000–3999, webhooks 4000–4999, organizations and environments 5000–5999, hardware keys 7000–7999.

Cross-cutting errors any endpoint can return.

Code Code name HTTP Meaning
1001 validation.empty 400 A required value was missing or empty.
1002 validation.duplicate 409 The value collides with an existing record.
1003 validation.failed 400 The request failed validation.
1010 resource.not_found 404 The resource doesn’t exist.
1011 resource.expired 410 The resource existed but has expired.
1012 resource.limit-exceeded 429 A plan or capacity limit was reached.
1020 operation.invalid 400 The operation isn’t valid in the current state.
1021 operation.pending 409 A conflicting operation is already in flight.
1030 auth.forbidden 403 The caller lacks the required scope or role.
1040 service.unavailable 503 Temporarily unavailable — retry with backoff. A request that has to look a user or an organization up, such as an activation or the caller’s own licenses, activation codes, plans and usage, answers it while the user directory can’t be reached, and changes nothing.
1041 service.failure 500 Unexpected failure.
1042 service.too-many-requests 429 Rate limit exceeded — honor Retry-After.

Returned by activation, session and usage endpoints. These can also answer service.unavailable (1040, under General above).

Code Code name HTTP Meaning
2001 license.no_active_license 403 No active license backs this request.
2002 license.no_such_entitlement 404 The entitlement doesn’t exist.
2003 license.no_active_session 409 No active session for this operation.
2004 license.not_found 404 The activation code doesn’t map to anything, or has expired.
2005 license.invalid_operation 400 The operation isn’t valid for this license’s state.
2006 license.no_permission 403 The caller doesn’t own this session or entitlement.
2007 license.no_such_product 404 The product doesn’t exist.
2008 license.no_such_provider 404 The provider doesn’t exist.
2009 license.validation_error 400 The request body or activation code is invalid.
2010 license.no_credit 400 The credit balance can’t cover the requested consumption.
2011 license.forbidden_by_policy 403 Blocked by the entitlement’s activation policy, for example offline activation turned off, a code issued to another user, or a YubiKey required. When something specific was missing, requires names it.
2012 license.operation_pending 409 A pending step (e.g. email verification) must complete first.
2013 license.expired 409 The license or credit balance has expired.
2014 license.spending_disabled 403 The balance holds credits, but its owner has switched extra-credit spending off. Distinct from license.no_credit: nothing needs buying.
2015 license.billing_events_not_included 403 Posting billing events isn’t included in your plan.
2016 license.reserved_scheme 403 A billing event names a reference under a scheme a built-in connector owns (stripe, sfdc, oclavex, manual, none, operator). Use your billing system’s own.
2017 license.platform_environment 403 The environment can’t take billing events.
2018 license.billed_elsewhere 409 Another provider bills the entitlement through a live subscription; it can take yours once that one is cancelled.
2019 license.provider_owns_end_date 400 A billing provider bills the entitlement or offer as a subscription and decides when it ends, so it takes no subscription policy or time restriction that ends it. Set its billing to managed by vendor to use one.
2020 license.offer_not_orderable 409 The offer isn’t Published, so it takes no orders: a Draft offer isn’t open yet, and a Disabled or Archived one no longer is. Publish a Draft offer to take orders from it.
2021 license.metric_not_tracked 404 A consume, or a preview of one, named a metric the entitlement doesn’t track, so nothing could be charged. Check the metric name, or add the metric to the plan behind the entitlement.
2022 license.session_token_rejected 401 The license token a session call presented is no longer accepted: its signing key, audience or lifetime doesn’t validate. Activate the license again; the SDK does this once on its own.
2023 license.hardware_keys_not_licensed 403 Hardware keys aren’t included in your plan: requiring one on an entitlement or offer, or activating a license that doesn’t require one with a hardware key. Licenses that already require a key keep activating.
2024 license.usage_metering_not_licensed 403 Your plan doesn’t include usage metering, so a new monitored asset, usage uploads, consumes and pre-allocations are refused and nothing is recorded. Monitored assets and credit balances are kept, and recording resumes once your plan includes usage metering again.
2025 license.unknown_user 403 The caller is signed in, but no user on your environment matches its sign-in, so it has no licenses, activation codes or entitlements of its own, and an activation that needs a user (useInteractiveUser, or a code issued to a user or an organization) is refused. Signing in again doesn’t change this: the user must exist under Customers → Users, and a user added after the refusal can take up to five minutes to be recognized.
2027 license.product_end_of_life 403 Every product the application may use is past its End of life, and the device has never activated the license, so it can’t start now, online or offline. Devices activated before keep activating and renewing. Clear the End of life on the product to let new devices activate again.
2028 license.authentication_required 401 The activation needs a signed-in user or a confirmed email address, and the caller has neither. requires says which (sign_in or verified_email); WWW-Authenticate carries the challenge. An offline request needing one is refused with license.forbidden_by_policy instead, since your operator who uploads it is signed in.

Returned by user and organization endpoints.

Code Code name HTTP Meaning
3001 organization.not_found 404 The user or organization doesn’t exist.
3002 organization.duplicate 409 Already exists (e.g. email in use).
3003 organization.invalid_operation 400 Not valid in the current state.
3004 organization.no_permission 403 The caller lacks directory permission.
3005 organization.validation_error 400 The request failed validation.
3006 organization.no_credit 400 A seat or capacity limit was reached.

Returned by the webhook subscription and delivery-management endpoints.

Code Code name HTTP Meaning
4001 subscription.tenant_required 400 The request lacks an active environment context.
4002 subscription.client_id_required 400 An OAuth client identity is required.
4003 subscription.tenant_mismatch 403 The subscription belongs to a different environment.
4010 subscription.not_found 404 The subscription doesn’t exist.
4020 subscription.invalid_state 409 Not allowed in the subscription’s current state (e.g. test-fire on an inactive subscription).
4021 subscription.invalid_transition 409 The requested state change isn’t allowed.
4022 subscription.not_applicable 400 The action has no meaning for this kind of subscription (e.g. rotating the secret of a capture subscription, which has no receiver).
4030 subscription.invalid_secret 400 The signing secret isn’t a Standard Webhooks secret: whsec_ followed by the base64 of 24 to 64 random bytes.
4031 subscription.invalid_overlap 400 The rotation overlap window is invalid.
4032 subscription.invalid_callback_scope 400 A requested callback scope isn’t allowed.
4033 subscription.invalid_url 400 The receiver URL is not allowed.
4034 subscription.invalid_filter 400 An attribute filter can never match: its field name is empty, its value is null, or no subscribed event type carries the field it names.
4035 subscription.invalid_event_type 400 An event-type pattern is not * or an exact type from the event catalog: family wildcards such as Entitlement.* are not accepted.
4040 subscription.challenge_failed 422 The endpoint failed the registration challenge.
4050 subscription.not_licensed 403 Webhooks aren’t included in your plan.
4060 replay.invalid_selection 400 A replay needs either envelopeIds or a from/to window — not both and not neither.
4061 replay.too_many_envelopes 400 The selection matches more envelopes than one replay request may carry; narrow the window or replay in batches.
4062 replay.rate_limited 429 The replay would exceed the hourly envelope quota.
4070 testfire.unknown_event_type 400 The requested event type isn’t in the event catalog.
4071 testfire.event_type_not_subscribed 400 The event type doesn’t match any of the subscription’s event-type patterns.
4072 testfire.event_type_not_allowed 400 The event type can’t be test-fired (the registration challenge is the platform’s own handshake).
4073 testfire.rate_limited 429 The subscription has used its test-fires for this hour.
4080 delivery.ambiguous_envelope 400 The envelope reached more than one subscription; pass subscriptionId to choose one.

Organizations and environments (5000–5999)

Section titled “Organizations and environments (5000–5999)”

Returned by the organization onboarding endpoints.

Code Code name HTTP Meaning
5001 onboarding.tenant_create_operator_only 403 During the private preview, new organizations are created by the Oclavex operator on request; an administrator of an existing organization can’t create one. Adding an environment to your own organization isn’t affected.

Returned by the hardware-key activation routes, online and offline.

Code Code name HTTP Meaning
7001 hardware_key.attestation_refused 403 The YubiKey’s attestation did not hold, so nothing was activated: it isn’t a genuine YubiKey 5, its firmware is older than 5.7, or it attests a different key than the one the device activates with. detail says which.
7002 hardware_key.proof_refused 403 An online activation’s proof that the device holds the YubiKey’s key did not hold, so nothing was activated: the proof is missing, signed by another key, made for another request, or its nonce has expired. detail says which.

Model-binding failures (malformed JSON, wrong types, missing required fields) return the standard ASP.NET validation problem — a 400 with an errors dictionary keyed by field name:

{
"status": 400,
"title": "One or more validation errors occurred.",
"errors": {
"activationCode": ["The activationCode field is required."]
}
}