Skip to content
Last updated

Error Handling

The DeepCredentials API uses standard HTTP status codes and structured error responses.

Error response format

All error responses follow this structure:

codestringrequired

Machine-readable error code (e.g. not_found, unauthenticated). See table below.

messagestringrequired

Human-readable error description.

detailsobject

Optional structured data with additional context.

Error codes

CodeHTTP StatusDescriptionRetryable
ok200No error.—
invalid_argument400Request validation failed (missing field, invalid format).No
unauthenticated401Missing, invalid, or expired credentials.No*
permission_denied403Valid credentials, but the token lacks the scope this endpoint requires, or the organization is not enrolled in the DeepCredentials beta (details.reason: beta_access_required).No
not_found404Resource does not exist or is not accessible to this credential.No
already_exists409The resource conflicts with an existing one (e.g. a verification case reference_id already in use — see below).No
aborted409A version check failed: a launch pinned a case version that is neither current nor kept launchable, or an update's expected_version is stale — see below.No*
resource_exhausted429Rate limit exceeded. Authenticated B2B calls are currently exempt from the shared rate limit.Yes (with backoff)
failed_precondition400Operation cannot be performed in the current state (e.g. an illegal credential-status transition, a credential definition not enabled in X-Environment, an archived verification case being changed, or a verification case launched in an environment it does not run in — see below).No
internal500Internal server error.Yes (with backoff)
unavailable503Service temporarily unavailable.Yes (with backoff)

* For expired Bearer tokens: re-authenticate against DeepCloud's SSO token endpoint to get a new access token (see Authentication — Service Users). For aborted: re-read the resource first; repeating the same request fails the same way.

Verification cases

A launch from a verification case (POST /b2b/v1/verification-cases/{caseId}/sessions), or a write to one, can be refused for reasons that name themselves in the message prefix and, where there is more to say, in details:

Message prefixCodeHTTP StatusMeaningWhat to do
case_not_available:failed_precondition400The case does not run in X-Environment: a credential it accepts is not enabled there. details carries reason, environment and missing_definitions (id, name), whoever's case it is.Launch in an environment the case lists in environments, or have the definition enabled there.
case_version_mismatch:aborted409case_version on a launch names a version that is neither current nor in the case's launchable_versions, or expected_version on an update names a version the case has moved on from, or another save landed between an update's read and its write.Re-read the case and upgrade or re-apply deliberately.
reference_id_conflict:already_exists409Creating or updating a case with a reference_id another case of your organization holds, archived cases included. details carries reason and case_id, naming that case.Use the existing case, or choose another key.
{
  "code": "failed_precondition",
  "message": "case_not_available: this case does not run in production: Badge is not enabled there",
  "details": {
    "reason": "case_not_available",
    "environment": "production",
    "missing_definitions": [{ "id": "5b0c…", "name": "Badge" }]
  }
}

Authentication errors

Tokens are issued by DeepCloud's SSO realm, not by DeepCredentials. Token-endpoint failures (e.g. wrong client_secret, disabled service user, missing scope on the partner client) follow OAuth 2.0 standard error responses from Keycloak:

ErrorHTTP StatusDescription
invalid_request400Missing or unsupported parameter on the token call.
invalid_client401client_id or client_secret rejected by Keycloak.
invalid_grant400username / password rejected, or the service user is disabled.
unauthorized_client403The partner client isn't configured for the requested grant type or scope.

If the token endpoint succeeds but the API still rejects the request: the response will use the DeepCredentials error format above. A 403 permission_denied means the token lacks the scope the endpoint requires, usually because your partner client wasn't configured to grant it — inspect the scope claim on the issued JWT (it lists what was actually granted, which may be a subset of what you requested).

Retry strategy

For retryable errors (429, 500, 503), use exponential backoff:

  1. Wait 1 second, then retry.
  2. If it fails again, wait 2 seconds.
  3. Double the wait time on each subsequent retry (4s, 8s, 16s...).
  4. Cap at 60 seconds maximum wait.
  5. Stop after 5 retries.

Add random jitter (±20%) to avoid thundering herd effects.

import time
import random

def retry_with_backoff(fn, max_retries=5):
    for attempt in range(max_retries):
        try:
            return fn()
        except RetryableError:
            if attempt == max_retries - 1:
                raise
            wait = min(60, (2 ** attempt)) * (0.8 + 0.4 * random.random())
            time.sleep(wait)
Do not retry client errors

Never retry 400, 401, 403, 404, or 409 errors — these indicate a problem with your request that must be fixed in code, or (for 409) a resource to re-read before you decide again.

Next steps