API error handling

The 10Duke Scale API uses conventional HTTP response codes to indicate the success or failure of an API request.

  • Codes in the 2xx range indicate success.

  • Codes in the 4xx range indicate a failure related to the information provided in or in context of the request (for example, a required parameter was omitted, an entity didn’t exist, or a heartbeat was sent too early).

  • Codes in the 5xx range indicate an error with 10Duke Scale servers (these are rare).

Codes in the 3xx range are currently not used. However, we recommend that API clients implement best practice security for HTTP redirects. The 10Duke Scale API may use redirects in the future, and the 304 Not Modified code may also be used in the future.

In addition to the HTTP status code, the API provides descriptive information when possible.

Note: In the case of an API error response, the API client must take action other than immediately retrying the same request. Notes about recommended actions or corrective measures are included with the HTTP response codes in this article.

General error responses

In most cases, the error body (payload) response from the 10Duke Scale API has the same shape.

The exceptions are:

  • Responses from earlier tiers in the network, such as firewalls, load balancing, and REST routing errors

  • License consumption denial responses from the 10Duke Scale License Checkout API

General HTTP error response format

The response header:

HTTP/1.1 {STATUS CODE} {STATUS NAME}
Date: 
Content-Type: application/json; charset=UTF-8
Content-Length: 104
Connection: keep-alive

The response body (payload):

{
  "error":"{STATUS NAME}",
  "code":{STATUS CODE},
  "description":"An error description"
}
Attribute Description
code The HTTP status code of the response (for example, 400).
error The error name or reason for the status code (for example, Bad Request).
description An error description providing more detail about why the request failed.

General HTTP response status codes

The following are the HTTP response status codes used by the 10Duke Scale API.

Status code Description
200 OK Everything worked as expected.
201 Created The request was successful, and a new entity was created as a result.
204 No Content There is no content to send for this request, but the headers may be useful. This status indicates success and is commonly used with API operations that delete entities (but not all delete operations).
400 Bad Request The request cannot be processed, typically due to a missing required parameter, a parameter that is not of the correct type, or a missing HTTP header. The API client must change the parameters/payload.
401 Unauthorized Authentication information is invalid or missing. Create or renew the API authentication token in the API client.
403 Forbidden The API token doesn’t have permissions to perform the request.
404 Not Found A provided entity identifier or natural key cannot be found. For more information, see the description field in the error response body, when available.
409 Conflict This can be caused by concurrent attempts to modify the same resource in separate API requests. The API client must refresh its data and start the modifying action again.
429 Too Many Requests This can be caused by too many requests to the API too quickly or by sending a license heartbeat too early. Follow the instructions of the specific API endpoint that returned the response. If no specific instructions are available, the recommended approach is an exponential backoff.
5xx Server Error The cause is internal to the 10Duke Scale API (these are rare).

Handling HTTP 429 (Too Many Requests) error

Your client applications must implement logic to handle 429 responses appropriately:

  • Check for HTTP status code 429 in all API responses.

  • Immediately reduce the rate of API calls when a 429 response is received.

  • Use an exponential backoff strategy before retrying the request.

We recommend that you Implement a robust retry mechanism with exponential backoff:

Initial retry delay: 1 second
Maximum retry delay: 60 seconds
Backoff multiplier: 2

Example retry sequence:
- First retry: Wait 1 second
- Second retry: Wait 2 seconds
- Third retry: Wait 4 seconds
- Fourth retry: Wait 8 seconds
- Fifth retry: Wait 16 seconds
- Sixth retry: Wait 32 seconds
- Seventh retry and beyond: Wait 60 seconds (max)

It’s also important to note the following additional recommendations:

  • If the API response includes a Retry-After header, use that value instead of the calculated backoff delay.

  • Implement a maximum retry limit (for example, 5-10 attempts) to avoid infinite retry loops.

  • Add random jitter (±20%) to retry delays to prevent thundering herd problems when multiple clients retry simultaneously.

  • Consider implementing a circuit breaker pattern to temporarily stop requests if repeated 429 responses occur.

  • Log all 429 responses to help identify patterns and optimize client behavior.

License consumption error responses

License consumption error responses refer to error responses from the License Checkout API when the API client attempted to check out or release a license in enforced consumption, to start or end metered consumption for a license, or to send a heartbeat for a license lease.

In most cases where a license consumption error occurred, the License Checkout API still returns HTTP 200 OK. This is due to a multi-entity response format where some parts may have been successful while some parts failed with an error.

When checking out a license, starting metered consumption, or sending a heartbeat, the response contains a JSON array of JSON Web Tokens (JWT), a JWT for each requested license. When releasing a checkout or ending metered use, the response contains an array of JSON objects, each representing the outcome for a specific license.

This means that to catch an error, for example, with a particular license checkout, the API client must parse the multi-entity response and handle it part by part. Each part holds the information on whether the request for the corresponding license was successful or failed.

In cases when there’s a fundamental error in the API request (for example, the license consumer cannot be found), the License Checkout API returns a general HTTP error.

License consumption error response format

The license checkout error uses the general HTTP response header.

The response format for each token in the JWT body:

[
  {
    "iat" : {ISSUED AT},
    "jti" : "{JWT ID}",
    "status" : "{ERROR STATUS}",
    "errorCode" : "{ERROR CODE}",
    "errorDescription": "An error description"
  }
]

The following are the claims relevant to error cases. See more details on the license checkout response and JWT claims if needed.

Claim Description
status If this claim has the value error, the client application must analyze the errorCode value to determine if it needs to communicate error details to the user.
errorCode The error code. Even if the client application doesn’t understand or know the given error code, it must treat the outcome as an error.
errorDescription An error description providing more detail about why the request failed. Note that this value is not localized, and we recommend that you don’t show it in your application UI. For UI messages, we recommend that you use the errorCode value to look up a message from your application’s language resources.

License consumption error codes

License tokens returned by checkout and heartbeat calls may indicate an error. We recommend that the client application handles the following errors by deleting the locally stored license token. Alternatively, the client application can set the license token as invalid to prevent the use of the application.

clientHwIdMismatch

The request failed because the license model binds the checkout to the hardware ID, and the hardware ID in the request doesn’t match the one provided at checkout.

Endpoints that can generate this error code: heartbeat and release

clientVersionMissing

The request failed because the client application version is required by the license but was not provided in the request.

Endpoints that can generate this error code: checkout and heartbeat

clientVersionNotAllowed

The request failed because the license doesn’t allow the client application version. With heartbeat requests this error can, for example, occur if the client application has been upgraded after the checkout. (Releasing/ending metered use is allowed even if the client application version is not allowed, and a checkout/start metered use request returns noLicenseFound instead.)

Endpoints that can generate this error code: checkout and heartbeat

insufficientQuantity

The checkout for a seat, use count, or use time from a license failed because the requested quantity exceeded the remaining quantity in the license. This error code is returned only when a license has available capacity, and the client application requested more quantity than the license has remaining. Note that this error code does not apply if all quantity in a license has already been checked out.

Endpoints that can generate this error code: checkout and heartbeat

leaseIdNotFound

The request failed because the license has been deleted.

Endpoints that can generate this error code: heartbeat and release

licenseCheckoutExpired

The request failed because the checkout has expired or the license has been revoked.

Endpoints that can generate this error code: heartbeat

licenseConsumerNotFound

The request failed because no license consumer was associated with the call. This error is given, for example, if the license consumer associated with a license key has been deleted but the license key has been left intact. The error applies to checkout/start metered use requests that use a license key.

Endpoints that can generate this error code: checkout

licenseConsumerVsConsumptionMismatch

The request failed because the checkout was made for a different license consumer than the one given in the request.

Endpoints that can generate this error code: heartbeat and release

licenseConsumptionClaimFieldsMissing

The request failed because it doesn’t specify all the client application claims required by the license checkout binding type, such as the client’s hardware ID claim.

Endpoints that can generate this error code: heartbeat and release

licenseConsumptionQtyEnforcementTypeMismatch

The request failed because the quantity enforcement type provided in the request does not match the one in the license. This error indicates that the client application has a defect that mixes license and checkout information.

Endpoints that can generate this error code: checkout, heartbeat, and release

licenseInvalid

The request failed because the license is currently not valid (for example, the license may have expired or may not be valid yet). This error applies to checkout/start metered use requests that specified a value in the licenseId parameter.

Endpoints that can generate this error code: checkout

licenseKeyVsLeaseIdMismatch

The request failed because the license key given in the request is for a different license than the checkout is for.

Endpoints that can generate this error code: heartbeat and release

noLicenseFound

The request failed because no license qualified for use based on the license consumption claims provided by the client application. This error is given, for example, if the licenses accessible to the license consumer require a different client application version than the version given in the checkout request. Note that this error code also applies when all quantity in all applicable licenses has already been checked out.

Endpoints that can generate this error code: checkout

Best practices for handling license consumption errors

  • When handling errors in a license consumption response, it’s essential to identify which parts failed and why. This includes inspecting each token’s errorCode and errorDescription.

  • We recommend that in error cases, the client application confirms further actions with the user of the application.

Example error responses

Here are some examples of API error responses.

Entity not found with given ID

All “read by ID” API endpoints respond with a Not Found error if the requested entity is not found with the given ID.

Example HTTP response headers in this case:

HTTP/1.1 404 Not Found
Date: Thu, 19 Jan 2023 18:25:51 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 99
Connection: keep-alive

An example HTTP response body:

{
  "error":"Not Found",
  "code":404,
  "description":"{ENTITY TYPE} [id=f83a406f-a1b2-4fc7-b687-00e3dcb3f0b3] does not exist"
}

In description, {ENTITY TYPE} is the API object type that was requested. For example, when requesting a Licensee by ID f83a406f-a1b2-4fc7-b687-00e3dcb3f0b3:

"description":"Licensee [id=f83a406f-a1b2-4fc7-b687-00e3dcb3f0b3] does not exist"

Checkout failed, license consumer not found

The license consumer that the license checkout is for is specified either in the sub claim in the OpenID Connect (OIDC) ID Token or in the licenseConsumerId header. If a license consumer by the given ID is not found, this results in an HTTP Not Found error response.

Example HTTP response headers in this case:

HTTP/1.1 404 Not Found
Date: Thu, 19 Jan 2023 18:25:51 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 99
Connection: keep-alive

An example HTTP response body:

{
  "error":"Not Found",
  "code":404,
  "description":"No such user: f83a406f-a1b2-4fc7-b687-00e3dcb3f0b3"
}

Another example HTTP response body:

{
  "error":"Not Found",
  "code":404,
  "description":"License consumer not found, make sure either licenseConsumerId is specified as a request parameter or use OIDC Id Token based API authorization"
}

Checkout failed, high concurrency/contention

Contention may happen if multiple client applications try to check out a seat from the same license at the exact same time. This happens if the concurrent checkout requests arrive within a timeframe of 100-200 milliseconds, and there are more requests than there are free seats for the license.

Example HTTP response headers in this case:

HTTP/1.1 409 Conflict
Date: Thu, 19 Jan 2023 18:25:51 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 104
Connection: keep-alive

An example HTTP response body:

{
  "error":"Conflict",
  "code":409,
  "description":"Contention to license suspected, locking checkout failed"
}

Checkout failed, no remaining quantity

A license may run out of capacity if there are more license consumers than the license has seats for, or if the use count or use time granted in the license has been consumed.

A license may also run out of capacity when the license model limits the number of concurrent devices per seat. For example, if an application can be used on two devices at the same time, and a user is already using it on a workstation and laptop, an attempt to check out the same seat on a third computer fails.

Example HTTP response headers in this case:

HTTP/1.1 200 OK
Date: Thu, 19 Jan 2023 18:25:51 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 99
Connection: keep-alive

An example HTTP response body, a JSON array of JWTs and a failed license check JWT body, looks like this:

[
  {
    "iat":1645784409,
    "jti":"d546a135-489e-4a77-accb-3798ff28fc74",
    "productName":"ThreeDee",
    "status":"error",
    "errorCode":"insufficientQuantity",
    "errorDescription":"No capacity left on license: ..."
  }
]

Heartbeat failed, lease ID not found

The License Checkout API may not find the lease ID if the checkout has been released, for example, by an administrator user through the 10Duke Scale UI console. In this case, the client application is holding a stale token and receives an error response when sending a heartbeat for the license checkout.

Example HTTP response headers in this case:

HTTP/1.1 200 OK
Date: Thu, 19 Jan 2023 18:25:51 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 104
Connection: keep-alive

An example HTTP response body, a JSON array of JWTs and a failed license check JWT body, looks like this:

[
  {
    "iat" : 1645784409,
    "jti" : "d546a135-489e-4a77-accb-3798ff28fc74",
    "status" : "error",
    "errorCode" : "leaseIdNotFound",
    "errorDescription": "The lease id: ... was not found"
  }
]