SignCareAPI Docs

Error Codes

All SignCare APIs return a consistent error shape. Here's how to handle each case.

Seeing an error code you don't recognise? The full searchable catalog is at 🤖 Ask AI (paste the error message) or the 📘 Error Code Catalog (browse by code). Both are updated whenever we add new error conditions.

Standard error envelope

Every error response follows this JSON shape:

{
  "success": false,
  "error": "ErrorCategory",
  "message": "Human-readable description of what went wrong.",
  "errorId": "uuid-for-support",
  "statusCode": 400
}

When a failure occurs deep inside a provider integration, you may also see:

{
  "success": false,
  "error": "ProviderError",
  "message": "...",
  "errorId": "...",
  "supportMessage": "Provide the ErrorId a1b2c3d4-e5f6 to the support team for further analysis."
}

Always log the errorId — our support team uses it to trace the full request lifecycle in our logs.

HTTP status code reference

StatusMeaningWhat you should do
200SuccessProcess the response body
400Bad RequestFix validation errors and retry
401UnauthorizedCheck your X-API-KEY / X-API-APP-ID headers
402Payment RequiredInsufficient credits — top up via portal
403ForbiddenEndpoint not enabled for your account
404Not FoundCheck URL path and input identifiers
408Request TimeoutRetry with backoff
410GoneResource expired (e.g. eSign link), create a new one
422Unprocessable EntitySemantic validation failed (e.g. invalid PAN format)
429Too Many RequestsHonor Retry-After, back off — see Rate Limits
500Internal Server ErrorTransient issue, retry with backoff; contact support if persistent
502Bad GatewayProvider upstream is unreachable, retry
503Service UnavailableProvider maintenance window, retry after a few minutes
504Gateway TimeoutProvider took too long, retry or poll async endpoint

Common error scenarios

401 — Missing or invalid X-API-KEY header

{
  "success": false,
  "error": "Unauthorized",
  "message": "Missing or invalid X-API-KEY header."
}

Cause: One or both auth headers are missing, empty, or misspelled.

Fix: Confirm your request includes both X-API-KEY and X-API-APP-ID with non-empty values.

400 — Validation error

{
  "success": false,
  "error": "ValidationError",
  "message": "The PAN number format is invalid.",
  "errors": {
    "pan": ["PAN must be 10 characters and match the pattern AAAAA0000A."]
  }
}

Cause: A request field failed format or business-rule validation.

Fix: Check the errors object for field-level messages and fix your input.

402 — Insufficient credits

{
  "success": false,
  "error": "InsufficientCredits",
  "message": "Your account has insufficient credits to call this endpoint."
}

Cause: Your prepaid credit balance is below the cost of this endpoint.

Fix: Top up via the SignCare portal or contact your account manager to adjust your credit terms.

429 — Rate limit exceeded

See Rate Limits for the full guide on handling 429.

500 — Internal server error

{
  "success": false,
  "error": "InternalServerError",
  "message": "An unexpected error occurred.",
  "errorId": "a1b2c3d4-e5f6-..."
}

Cause: A bug, unhandled edge case, or infrastructure issue on our side.

Fix: Retry once with backoff. If it persists, contact support@signcare.io with the errorId.

Provider-specific errors

When we proxy to a government or financial provider and the provider returns a failure, we surface it as:

{
  "success": false,
  "error": "ProviderError",
  "message": "The PAN does not exist in the NSDL database.",
  "providerCode": "NSDL_PAN_NOT_FOUND",
  "errorId": "..."
}

These are user-level errors — your user gave a bad input or the provider genuinely has no record. Show a friendly message to your user; do not retry.

Designing your error handling

A robust client should handle these four classes differently:

ClassCodesStrategy
Authentication401, 403Surface to ops team; don't retry
Validation400, 422Surface to end user; don't retry
Rate limit429Back off + retry
Transient server500, 502, 503, 504Retry 2-3 times with backoff

Example pattern:

async function callSignCare(path, body) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const res = await fetch(`${BASE_URL}${path}`, {
      method: 'POST',
      headers: {
        'X-API-KEY': API_KEY,
        'X-API-APP-ID': APP_ID,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    });
 
    if (res.ok) return res.json();
 
    // Don't retry client errors
    if (res.status >= 400 && res.status < 500 && res.status !== 429) {
      throw await res.json();
    }
 
    // Retry transient + rate limits
    const retryAfter = parseInt(res.headers.get('retry-after') ?? '0', 10) * 1000;
    await new Promise((r) => setTimeout(r, retryAfter || Math.pow(2, attempt) * 1000));
  }
  throw new Error('All retry attempts failed');
}

On this page