Skip to main content
Every Platform API error follows the RFC 7807 Problem Details format. This page covers common error scenarios, retry strategies, and domain-specific error codes.

Error Format

{
  "type": "https://api.sumvin.com/errors/wal-404-001",
  "title": "Wallet Not Found",
  "status": 404,
  "detail": "No wallet found with ID 12345",
  "instance": "/v0/wallets/12345",
  "error_code": "WAL-404-001",
  "trace_id": "abc-123-def"
}
FieldDescription
typeA URI identifying the error type
titleA short, human-readable summary
statusThe HTTP status code
detailA human-readable explanation specific to this occurrence
instanceThe request path that generated the error
error_codeMachine-readable code in {DOMAIN}-{STATUS}-{SEQUENCE} format
trace_idUnique identifier for this request — share with support for debugging

Common HTTP Status Codes

StatusMeaningAction
200Success
201CreatedResource created successfully
202AcceptedAsync operation started — poll _links for completion
208Already ReportedIdempotent replay — resource already exists, returned as-is
400Bad RequestFix the request body or parameters
401UnauthorizedCheck your JWT or API key
403ForbiddenValid credentials but insufficient permissions
404Not FoundResource doesn’t exist — check the ID
409ConflictResource state conflict (e.g., duplicate wallet)
422UnprocessableMissing or invalid required fields
424Failed DependencyUpstream dependency not ready (e.g., agent signer not provisioned)
429Too Many RequestsRate limited — wait for Retry-After header duration
500Internal ErrorRetry with exponential backoff

Authentication Errors

Error CodeStatusDescriptionResolution
USR-401-001401Missing or invalid JWTCheck x-juno-jwt header contains a valid, unexpired token
USR-404-001404No user exists for this JWTCreate the user first with POST /v0/user/

User & Onboarding Errors

Error CodeStatusDescriptionResolution
USR-409-001409User already existsUse the existing user — POST /v0/user/ returns 208 Already Reported
USR-422-001422Missing required fields on user creationInclude primary_eoa_address and chain_id

Wallet Errors

Error CodeStatusDescriptionResolution
WAL-400-001400Invalid Ethereum address formatProvide a valid 0x-prefixed, checksummed address
WAL-400-002400Chain ID does not match primary chainUse the same chain as the primary wallet
WAL-403-001403Cannot delete primary walletSet another wallet as primary first
WAL-403-002403Wallet belongs to another userVerify the wallet ID
WAL-403-003403Wallet address not in verified credentialsVerify wallet ownership — see Wallets Guide
WAL-404-001404Wallet not foundCheck the wallet ID; list wallets via GET /v0/wallets
WAL-409-001409Wallet already exists for this address and chainUse the existing wallet
USR-424-001424No active agent signerWait for onboarding to complete before setting a primary wallet

KYC Errors

Error CodeStatusDescriptionResolution
KYC-403-001403KYC already submittedCheck status with GET /v0/kyc/status instead of re-submitting
KYC-409-001409KYC session already in progressWait for the existing session to complete

Retry Strategy

Retryable Errors

These errors are transient and should be retried with exponential backoff:
StatusStrategy
429Wait for the Retry-After header value, then retry
500Retry up to 3 times with exponential backoff (1s, 2s, 4s)
424Retry after a delay — the upstream dependency may become available
202Not an error — poll the linked resource for completion

Non-Retryable Errors

These indicate a problem with the request itself:
StatusAction
400Fix the request body or parameters
401Refresh or replace the authentication token
403Check permissions — the action may not be allowed in the current state
404Verify the resource ID exists
409Resolve the conflict — typically by using the existing resource

Example: Retry with Backoff

async function apiCall(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.ok || response.status === 208) return response;

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get("Retry-After") ?? "30");
      await new Promise((r) => setTimeout(r, retryAfter * 1000));
      continue;
    }

    if (response.status >= 500 && attempt < maxRetries) {
      await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 1000));
      continue;
    }

    const error = await response.json();
    throw new ApiError(error.error_code, error.detail, error.trace_id);
  }
}

SIS Error Codes

For error codes from the Sumvin Identity Service (token exchange, revocation), see the SIS Error Reference.