Error Handling

Overview

The FastMint and Burn API uses standard HTTP status codes and consistent JSON error responses to indicate the success or failure of requests.

Response Format

Success Response

{
  "success": true,
  "data": {
    // Response data
  }
}

Error Response

{
  "success": false,
  "message": "Human-readable error message",
  "error": {
    // Optional additional error details
  }
}

HTTP Status Codes

Status Code
Meaning
Common Causes

200

OK

Successful GET request

201

Created

Successful POST request creating a resource

400

Bad Request

Invalid request parameters or body

401

Unauthorized

Missing, invalid, or expired JWT token

403

Forbidden

Valid token but insufficient permissions or inactive wallet

404

Not Found

Resource or endpoint not found

500

Internal Server Error

Server-side error

Common Error Responses

Authentication Errors

Missing API Key

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "success": false,
  "message": "Missing or malformed X-API-Key"
}

Cause: X-API-Key header not provided or incorrectly formatted

Resolution: Include X-API-Key: <keyId>.<secret> header

Invalid API Key

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "success": false,
  "message": "Invalid API key"
}

Cause: API key not found, inactive, or expired

Resolution: Verify API key is correct and active; contact support if needed

Missing JWT Token

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "success": false,
  "message": "Missing authorization header"
}

Cause: Authorization header not provided

Resolution: Include Authorization: Bearer <token> header

Invalid JWT Token

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "success": false,
  "message": "Invalid token signature"
}

Cause: Token signature invalid, token tampered with, or wrong issuer

Resolution: Obtain a new token via /token

Expired JWT Token

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "success": false,
  "message": "Token expired"
}

Cause: Token expiration time (exp claim) has passed

Resolution: Refresh token via /token/refresh or issue new token

Inactive Wallet

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "success": false,
  "message": "Wallet is not active"
}

Cause: User wallet has been deactivated

Resolution: Contact support to reactivate wallet

Mint API Errors

Wallet Already Exists

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "A wallet already exists for this chain and destination combination"
}

Cause: Attempting to create duplicate deposit address for same chain/destination pair

Resolution: Use existing wallet from GET /mint/wallets

Invalid Source Chain

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Unsupported source chain"
}

Cause: Invalid chain symbol

Resolution: Use valid chain: sol, xrp, sui, doge, zec

Invalid Destination Chain

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Unsupported destination chain"
}

Cause: Invalid chain symbol

Resolution: Use valid chain: base, arbitrum, katana, world, unichain

Burn API Errors

Burn Wallet Already Exists

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Wallet already exists for this chain"
}

Cause: Attempting to create duplicate burn configuration for same destination chain

Resolution: Update existing configuration via PUT /burn/wallets or use existing one

Invalid Destination Address Format

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Invalid Solana address format. Must be base58 encoded, 32-44 characters."
}

Cause: Destination address doesn't match expected format for the chain

Resolution: Verify address format matches chain requirements:

  • Solana: base58, 32-44 chars

  • XRP: starts with 'r', 25-34 chars

  • Sui: hex with 0x prefix, 66 chars

  • Dogecoin: starts with 'D', 'A', or '9', 34 chars

  • Zcash: starts with t1, 35 chars

Missing XRP Memo

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Memo is required for XRP and TXRP chains"
}

Cause: Memo not provided for XRP destination

Resolution: Include numeric memo field (1-10 digits, max: 4294967295)

Invalid XRP Memo Format

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Memo must be numeric for XRP and TXRP chains"
}

Cause: Memo contains non-numeric characters

Resolution: Use only numeric digits (0-9) in memo

XRP Memo Too Large

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Memo must not exceed maximum value (4294967295)"
}

Cause: Memo exceeds 32-bit unsigned integer limit

Resolution: Use memo value ≤ 4294967295

Amount Below Minimum

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Amount below minimum burn threshold"
}

Cause: Burn amount below minimum for the token

Resolution: Ensure amount meets minimum:

  • uSOL, uXRP, uSUI: 1.0 tokens

  • uBTC: 0.0001 tokens

  • uDOGE, uZEC: 0.1 tokens

Order API Errors

Failed to Retrieve Orders

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "success": false,
  "message": "Failed to retrieve orders"
}

Cause: Database or internal error

Resolution: Retry request; contact support if persists

Validation Errors

Invalid Request Body

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Invalid request body",
  "error": {
    "issues": [
      {
        "path": ["destinationChain"],
        "message": "Invalid enum value. Expected 'sol' | 'xrp' | 'sui' | 'doge' | 'zec'"
      }
    ]
  }
}

Cause: Request body fails Zod schema validation

Resolution: Check error.issues for specific field errors

Missing Required Field

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "success": false,
  "message": "Validation error",
  "error": {
    "issues": [
      {
        "path": ["walletAddress"],
        "message": "Required"
      }
    ]
  }
}

Cause: Required field missing from request

Resolution: Include all required fields

Error Handling Best Practices

1. Implement Retry Logic

async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);

      // Don't retry client errors (4xx)
      if (response.status >= 400 && response.status < 500) {
        throw new Error(await response.text());
      }

      // Retry server errors (5xx)
      if (response.status >= 500) {
        if (i === maxRetries - 1) throw new Error('Max retries exceeded');
        await sleep(Math.pow(2, i) * 1000); // Exponential backoff
        continue;
      }

      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

2. Handle Token Expiration

async function makeAuthenticatedRequest(url, token) {
  const response = await fetch(url, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  if (response.status === 401) {
    // Token expired, refresh and retry
    const newToken = await refreshToken();
    return makeAuthenticatedRequest(url, newToken);
  }

  return response.json();
}

3. Validate Before Sending

async function createMintWallet(chain, destinationChain) {
  // Validate against config first
  const config = await fetch('https://api.universal.xyz/issuance-redemption/config/mint').then((r) => r.json());

  const validSource = config.data.sourceChains.some((c) => c.symbol === chain && c.type === 'main');

  const validDestination = config.data.destinationChains.some(
    (c) => c.symbol === destinationChain && c.type === 'main',
  );

  if (!validSource || !validDestination) {
    throw new Error('Invalid chain combination');
  }

  // Proceed with request
  return fetch('/mint/wallets', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ chain, destinationChain }),
  });
}

4. Log Errors Properly

async function handleApiError(error, context) {
  const errorLog = {
    timestamp: new Date().toISOString(),
    context,
    status: error.status,
    message: error.message,
    details: error.error,
  };

  // Log to your monitoring system
  console.error('API Error:', JSON.stringify(errorLog));

  // Alert on specific errors
  if (error.status === 403 && error.message.includes('Wallet is not active')) {
    await alertSupport('Wallet deactivation detected', errorLog);
  }
}

5. Graceful Degradation

async function getMintWallets(token) {
  try {
    const response = await fetch('/mint/wallets', {
      headers: { Authorization: `Bearer ${token}` },
    });

    if (!response.ok) {
      throw new Error('Failed to fetch wallets');
    }

    return await response.json();
  } catch (error) {
    // Return cached data or empty array on error
    console.error('Error fetching wallets:', error);
    return { success: false, data: [] };
  }
}

Rate Limiting

While not currently enforced, be mindful of request frequency:

  • Token Issuance: Cache tokens and reuse; don't issue new tokens for every request

  • Orders Polling: Poll every 10-30 seconds, not more frequently

  • Configuration: Cache config responses for hours, not minutes

Support and Troubleshooting

If you encounter persistent errors:

  1. Check Status Page: Verify API availability

  2. Review Logs: Check error messages and status codes

  3. Validate Input: Ensure request format matches documentation

  4. Contact Support: Email [email protected] with:

    • Error message and status code

    • Request details (sanitize sensitive data)

    • Timestamp of error

    • Your partner ID

Next Steps

Last updated