Error Codes Reference

Complete reference for all error codes, HTTP statuses and debugging tips for the Sovera Integration Layer API.

Reference every error code the Sovera Integration Layer API returns.

Errors are predictable. Every failure—whether a missing field, a revoked key, or an upstream outage—comes back in one envelope with the same shape. Branch your logic on the HTTP status for the broad category and on the code for the exact reason; treat the human-readable message as a hint to log or surface, not something to parse. Each code is derived from the error: the API first matches keywords in the message (for example, a "not found" about a user yields USER_NOT_FOUND), then falls back to a code based on the HTTP status.

Error Response Format

Every error returns success: false, an errors array, and a meta block. The errors array carries one or more objects, each with a stable code and a message. The meta block carries the timestamp, the API version, and a trace_id that ties the response to server-side logs—capture it for every failed request.

{
  "success": false,
  "errors": [
    {
      "code": "ERROR_CODE",
      "message": "Human-readable error description"
    }
  ],
  "meta": {
    "timestamp": "2026-03-12T00:00:00.000Z",
    "version": "v1",
    "trace_id": "abc-123-def-456"
  }
}

HTTP Status Codes

StatusMeaningWhen It Occurs
200OKRequest successful
201CreatedResource created successfully
400Bad RequestInvalid request data or business rule violation
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but not permitted
404Not FoundResource doesn't exist
405Method Not AllowedHTTP method not supported
409ConflictResource already exists
413Payload Too LargeRequest body too large
415Unsupported Media TypeWrong Content-Type
422Unprocessable EntitySemantically invalid request
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error
502Bad GatewayUpstream service error (Utila/SFOX)
503Service UnavailableService temporarily unavailable
504Gateway TimeoutUpstream service timed out

Authentication & Authorization Errors

CodeHTTP StatusMessageCause
UNAUTHORIZED401Varies (for example, "API key is required", "Invalid API key", "Client is inactive")Missing, invalid, revoked, or inactive x-api-key; missing or invalid master API key
FORBIDDEN403Varies (for example, "Access denied")Authenticated but lacking permission for the resource

The code for authentication failures is always UNAUTHORIZED (401) or FORBIDDEN (403); the specific reason is in the message. A 401 means the credential is missing, malformed, revoked, or expired—fix the header or mint a fresh user token. A 403 means the credential is valid but lacks reach—switch to the API key (or master key) the endpoint expects.

How to fix:

# Owner API Key (most User API endpoints)
curl -H "x-api-key: YOUR_OWNER_API_KEY" ...

# Re-generate a user token
curl -X POST "BASE_URL/users/{user_id}/token" \
  -H "x-api-key: YOUR_OWNER_API_KEY"

Validation Errors

CodeHTTP StatusMessageCause
UNPROCESSABLE_ENTITY422Field-specific messageMissing required fields, wrong types, invalid format
BAD_REQUEST400Human-readable descriptionGeneric bad request or business-rule violation

Field validation fails with 422 Unprocessable Entity. The errors array carries one entry per invalid field, so you can map each failure back to its input.

Example:

{
  "success": false,
  "errors": [
    {
      "code": "UNPROCESSABLE_ENTITY",
      "message": "email must be a valid email address"
    },
    {
      "code": "UNPROCESSABLE_ENTITY",
      "message": "phone_number must be a valid mobile number for the country"
    }
  ],
  "meta": {
    "timestamp": "2026-03-12T00:00:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

User Errors

CodeHTTP StatusMessageCause
USER_NOT_FOUND404User not foundIncorrect user_id or user belongs to different client
RESOURCE_NOT_FOUND404User not found or inactiveUser is deleted or inactive
EMAIL_ALREADY_EXISTS409User with this email already exists for this customerDuplicate email on create/update
PHONE_ALREADY_EXISTS409User with this phone number already exists for this customerDuplicate phone on create/update
RESOURCE_CONFLICT409User with this user_id already existsDuplicate user_id
INVALID_USER_TYPE400Invalid user typeUnsupported user type value
INVALID_ACCOUNT_TYPE400account_type must be one of: individual, corporateWrong enum value
INVALID_ACCOUNT_ROLE400account_role must be one of: thirdWrong enum value
INVALID_ACCOUNT_PURPOSE400account_purpose must be one of: trading, investingWrong enum value
INVALID_AGE400User must be at least 18 years oldDOB fails age validation
CANNOT_DELETE_ACTIVE_USER400Cannot delete an active userDeactivate user before deleting
CANNOT_UPDATE_DELETED_USER400Cannot update a deleted userUser is soft-deleted
CLIENT_NO_SUPPLIER_ID400Client does not have a supplier_custody_id configuredAdmin must configure vault first
TOKEN_GENERATION_FAILED500Failed to generate user tokenInternal error during token creation

Wallet Errors

CodeHTTP StatusMessageCause
WALLET_NOT_FOUND404Wallet not foundIncorrect wallet_id or wallet belongs to different user
ADDRESS_NOT_FOUND404Wallet address not foundNo address for given currency
WALLET_INACTIVE400Wallet is inactiveWallet has been deactivated
WALLET_LOCKED400Wallet is lockedWallet temporarily locked
WALLET_SUSPENDED400Wallet is suspendedContact admin
INVALID_WALLET_ID400Invalid wallet IDMalformed wallet ID format
WALLET_NAME_REQUIRED400Wallet name is requiredMissing name field
WALLET_NAME_MAX_LENGTH400Wallet name exceeds maximum length (100 chars)Name too long
RESOURCE_CONFLICT409Wallet with name already exists for this userDuplicate wallet name
INVALID_CURRENCY400Invalid currency codeUnsupported or misspelled currency
UNSUPPORTED_CURRENCY400Unsupported currencyCurrency not enabled for vault
CURRENCY_NOT_ACTIVE400Currency is not activeCurrency disabled
ADDRESS_ALREADY_EXISTS409Address already exists for this walletAddress for this currency already created
ADDRESS_GENERATION_FAILED500Failed to generate wallet addressUtila/SFOX error during address creation
INSUFFICIENT_BALANCE400Insufficient balanceAvailable balance too low for operation
SAME_WALLET_TRANSFER400Cannot transfer to the same walletfrom_wallet_id equals to_wallet_id
AMOUNT_MUST_BE_POSITIVE400Amount must be positiveZero or negative amount
AMOUNT_TOO_SMALL400Amount must be at least 0.000001Below minimum transfer threshold
TRANSFER_LIMIT_EXCEEDED400Transfer amount exceeds limitPer-transaction cap exceeded
DAILY_LIMIT_EXCEEDED400Daily transfer limit exceededDaily cap reached
MONTHLY_LIMIT_EXCEEDED400Monthly transfer limit exceededMonthly cap reached

Trading / Order Errors

CodeHTTP StatusMessageCause
QUOTE_NOT_FOUND404Quote not foundQuote ID invalid or belongs to different client
QUOTE_EXPIRED400Quote has expiredQuotes expire quickly — fetch a fresh one
QUOTE_INVALID400Invalid quoteQuote already used or malformed
INVALID_CURRENCY_PAIR400Invalid currency pairPair not supported
PAIR_NOT_AVAILABLE400Currency pair not availablePair disabled or unavailable
INVALID_SIDE400Invalid side valueMust be buy or sell
QUANTITY_REQUIRED400Quantity is requiredMissing quantity field
QUANTITY_MUST_BE_POSITIVE400Quantity must be positiveZero or negative quantity
INSUFFICIENT_BALANCE400Insufficient balance for orderNot enough funds to execute
MINIMUM_ORDER_NOT_MET400Order amount below minimumBelow minimum order size
MAXIMUM_ORDER_EXCEEDED400Order amount exceeds maximumExceeds per-order cap
MARKET_CLOSED400Market is closedTrading outside market hours
LIQUIDITY_INSUFFICIENT400Insufficient liquidityNot enough market depth
PRICE_CHANGED400Price has changed since quoteRe-fetch quote and retry
ORDER_FAILED400Order failedSFOX rejected the order

Withdrawal Errors

CodeHTTP StatusMessageCause
WITHDRAWAL_NOT_FOUND404Withdrawal not foundIncorrect withdrawal_id
INVALID_CRYPTO_ADDRESS400Invalid cryptocurrency addressWrong format or wrong network
INVALID_ADDRESS400Invalid destination addressAddress validation failed
INVALID_AMOUNT400Invalid amountZero, negative, or non-numeric
AMOUNT_MUST_BE_POSITIVE400Amount must be positiveNegative or zero amount
INSUFFICIENT_BALANCE400Insufficient balance for withdrawalAvailable balance too low
WITHDRAWAL_LIMIT_EXCEEDED400Withdrawal amount exceeds limitPer-withdrawal cap exceeded
DAILY_LIMIT_EXCEEDED400Daily withdrawal limit exceededDaily cap reached
MONTHLY_LIMIT_EXCEEDED400Monthly withdrawal limit exceededMonthly cap reached
MINIMUM_AMOUNT_NOT_MET400Withdrawal amount below minimumBelow minimum threshold
CONFIRMATION_CODE_INVALID400Invalid confirmation codeWrong PIN entered
CONFIRMATION_CODE_EXPIRED400Confirmation code has expiredPIN expired — initiate a new withdrawal
ALREADY_CONFIRMED400Withdrawal already confirmedDo not re-submit a confirmed withdrawal
INVALID_WEBHOOK_SIGNATURE401Invalid webhook signatureWebhook secret mismatch
PIN_NOTIFICATION_FAILED400Failed to send PIN notificationEmail delivery issue
BANK_ACCOUNT_REQUIRED400Bank account information is requiredMissing fiat withdrawal bank details
INVALID_BANK_ACCOUNT400Invalid bank account informationBank details failed validation
CRYPTO_WITHDRAWAL_FAILED400Crypto withdrawal failedUpstream error from Utila/SFOX
FIAT_WITHDRAWAL_FAILED400Fiat withdrawal failedUpstream fiat processing error

System & Gateway Errors

CodeHTTP StatusMessageCause
INTERNAL_SERVER_ERROR500Internal server errorUnexpected error — include trace_id when reporting
BAD_GATEWAY502Bad gatewayUpstream service (Utila or SFOX) returned an error
SERVICE_UNAVAILABLE503Service temporarily unavailableMaintenance or overload
GATEWAY_TIMEOUT504Gateway timeoutUpstream service did not respond in time
NOT_IMPLEMENTED501Not implementedFeature not yet available
RATE_LIMIT_EXCEEDED429Too many requestsSlow down and implement backoff
PAYLOAD_TOO_LARGE413Payload too largeRequest body exceeds size limit
UNSUPPORTED_MEDIA_TYPE415Unsupported media typeSet Content-Type: application/json
METHOD_NOT_ALLOWED405Method not allowedWrong HTTP verb for this endpoint

Debugging Tips

Trace IDs

Every response carries a trace_id in meta that correlates it with server-side logs. Log it for every failed request and include it whenever you report an issue—it's the fastest way for support to find the exact call.

{
  "meta": {
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

Or pass your own x-trace-id to stitch a request to your application logs end-to-end. The API echoes it back instead of generating one:

curl -H "x-trace-id: my-custom-trace-123" \
  -H "x-api-key: YOUR_API_KEY" \
  BASE_URL/users

Common Troubleshooting

  1. 401 Unauthorized — Check x-api-key header is present and lowercase, key is not revoked
  2. 403 Forbidden — Endpoint requires Owner API Key or higher permission level
  3. 404 Not Found — Verify the resource ID and that it belongs to your client account
  4. 409 Conflict — A resource with that email, phone, or name already exists
  5. 400 Bad Request — Read the message field — it will point to the specific field or rule that failed
  6. 429 Too Many Requests — Implement exponential backoff:
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.response?.status === 429) {
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Max retries exceeded');
}
  1. 502 / 504 — External service (Utila or SFOX) issue. Retry after a short delay.

Error Handling Example

import axios from 'axios';

async function handleApiCall() {
  try {
    const response = await axios.get('BASE_URL/users', {
      headers: { 'x-api-key': 'YOUR_API_KEY' }
    });
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      const apiError = error.response?.data;
      const code = apiError?.errors?.[0]?.code;
      const message = apiError?.errors?.[0]?.message;
      const traceId = apiError?.meta?.trace_id;

      console.error(`API Error [${code}]: ${message} (trace: ${traceId})`);

      switch (code) {
        case 'UNAUTHORIZED':
          // Re-authenticate
          break;
        case 'RATE_LIMIT_EXCEEDED':
          // Back off and retry
          break;
        case 'QUOTE_EXPIRED':
          // Fetch a new quote and retry
          break;
        default:
          throw error;
      }
    }
    throw error;
  }
}

Support

Hit persistent errors? Contact support with:

  • Trace ID from the error response
  • Timestamp of the request
  • Endpoint and HTTP method called
  • Request body / parameters (redact sensitive values)
  • Email: [email protected]

On this page