> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fyatu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Fyatu API error codes and HTTP status codes. Handle validation errors, insufficient balance, invalid tokens, and provider failures in your card issuing integration.

# Error Handling

The FYATU API uses conventional HTTP response codes and returns detailed error messages with machine-readable error codes to help you handle errors programmatically.

## Response Format

All API responses follow a consistent format:

<CodeGroup>
  ```json Success Response theme={null}
  {
    "success": true,
    "status": 200,
    "message": "Operation completed successfully",
    "data": {
      // Response data here
    },
    "meta": {
      "requestId": "req_7af4d2b8e91c35fa",
      "timestamp": "2026-01-05T10:30:00+00:00"
    }
  }
  ```

  ```json Error Response theme={null}
  {
    "success": false,
    "status": 400,
    "message": "Validation failed",
    "error": {
      "code": "VALIDATION_ERROR",
      "details": [
        { "field": "amount", "message": "Amount is required" }
      ]
    },
    "meta": {
      "requestId": "req_7af4d2b8e91c35fa",
      "timestamp": "2026-01-05T10:30:00+00:00"
    }
  }
  ```
</CodeGroup>

<Tip>
  Always include the `requestId` from the `meta` object when contacting support — it helps us trace your request through our systems.
</Tip>

## HTTP Status Codes

| Code  | Status               | Description                                          |
| ----- | -------------------- | ---------------------------------------------------- |
| `200` | OK                   | Request succeeded                                    |
| `201` | Created              | Resource created successfully                        |
| `400` | Bad Request          | Invalid request parameters or validation failed      |
| `401` | Unauthorized         | Authentication failed or token expired               |
| `402` | Payment Required     | Insufficient wallet balance                          |
| `403` | Forbidden            | Access denied (scope mismatch or resource not owned) |
| `404` | Not Found            | Resource doesn't exist                               |
| `409` | Conflict             | Duplicate reference or conflicting operation         |
| `422` | Unprocessable Entity | Request understood but cannot be processed           |
| `429` | Too Many Requests    | Rate limit exceeded                                  |
| `500` | Server Error         | Something went wrong on our end                      |

## Error Codes

Each error response includes a machine-readable `code` field:

| Code                       | HTTP | Description                                             |
| -------------------------- | ---- | ------------------------------------------------------- |
| `AUTH_TOKEN_MISSING`       | 401  | No authorization token provided                         |
| `AUTH_TOKEN_INVALID`       | 401  | Token is malformed, expired, or revoked                 |
| `AUTH_INVALID_CREDENTIALS` | 401  | App ID or secret key is incorrect                       |
| `AUTH_SCOPE_DENIED`        | 403  | Token doesn't have the required scope for this endpoint |
| `VALIDATION_ERROR`         | 400  | One or more request fields failed validation            |
| `RESOURCE_NOT_FOUND`       | 404  | The requested resource does not exist                   |
| `INSUFFICIENT_BALANCE`     | 402  | Wallet balance is too low for this operation            |
| `DUPLICATE_REFERENCE`      | 409  | The `externalReference` has already been used           |
| `RATE_LIMIT_EXCEEDED`      | 429  | Too many requests — slow down                           |
| `INTERNAL_ERROR`           | 500  | Unexpected server error                                 |

## Common Error Scenarios

### Authentication Errors (401)

```json Missing Token theme={null}
{
  "success": false,
  "status": 401,
  "message": "Authorization token is required",
  "error": { "code": "AUTH_TOKEN_MISSING" }
}
```

**Solution**: Include the `Authorization: Bearer {token}` header in your request.

```json Expired Token theme={null}
{
  "success": false,
  "status": 401,
  "message": "Invalid or expired token",
  "error": { "code": "AUTH_TOKEN_INVALID" }
}
```

**Solution**: Refresh your token via `POST /v3/auth/refresh` or request a new one via `POST /v3/auth/token`.

```json Insufficient Scope theme={null}
{
  "success": false,
  "status": 403,
  "message": "Access denied. Required scope: cards:write",
  "error": { "code": "AUTH_SCOPE_DENIED" }
}
```

**Solution**: Your app type doesn't have access to this endpoint. A Collection App cannot access Card endpoints and vice versa.

### Validation Errors (400)

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "Validation failed",
  "error": {
    "code": "VALIDATION_ERROR",
    "details": [
      { "field": "amount", "message": "Amount must be greater than 0" },
      { "field": "email", "message": "The email field must contain a valid email address" }
    ]
  }
}
```

**Solution**: Check the `details` array for specific field requirements and fix the request payload.

### Insufficient Balance (402)

```json theme={null}
{
  "success": false,
  "status": 402,
  "message": "Insufficient balance. Required: $105.00, Available: $50.00",
  "error": { "code": "INSUFFICIENT_BALANCE" }
}
```

**Solution**: Fund your business wallet before retrying. The required amount includes applicable fees.

### Resource Not Found (404)

```json theme={null}
{
  "success": false,
  "status": 404,
  "message": "Card not found",
  "error": { "code": "RESOURCE_NOT_FOUND" }
}
```

**Solution**: Verify the resource ID exists and belongs to your app.

### Duplicate Reference (409)

```json theme={null}
{
  "success": false,
  "status": 409,
  "message": "A collection with this external reference already exists",
  "error": { "code": "DUPLICATE_REFERENCE" }
}
```

**Solution**: Use a unique `externalReference` for each new operation.

### Rate Limiting (429)

```json theme={null}
{
  "success": false,
  "status": 429,
  "message": "Rate limit exceeded. Try again in 60 seconds.",
  "error": { "code": "RATE_LIMIT_EXCEEDED" }
}
```

**Solution**: Implement exponential backoff in your retry logic.

## Error Handling Best Practices

### 1. Always Check the Response

```javascript theme={null}
const response = await fetch('https://api.fyatu.com/api/v3/cards', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(cardData)
});

const result = await response.json();

if (!result.success) {
  console.error(`API Error [${result.error?.code}]: ${result.message}`);
  console.error('Request ID:', result.meta?.requestId);
  return;
}

// Process successful response
console.log('Card created:', result.data.cardId);
```

### 2. Handle Specific Error Codes

```javascript theme={null}
async function createCollection(data) {
  const result = await fyatuRequest('POST', '/v3/collections', data);

  if (result.success) {
    return { ok: true, collection: result.data };
  }

  switch (result.error?.code) {
    case 'AUTH_TOKEN_INVALID':
      // Token expired — refresh and retry
      await refreshToken();
      return createCollection(data);

    case 'VALIDATION_ERROR':
      return { ok: false, fields: result.error.details };

    case 'INSUFFICIENT_BALANCE':
      return { ok: false, error: 'Please fund your wallet' };

    case 'DUPLICATE_REFERENCE':
      return { ok: false, error: 'This order was already submitted' };

    case 'RATE_LIMIT_EXCEEDED':
      await sleep(60000);
      return createCollection(data);

    default:
      return { ok: false, error: result.message };
  }
}
```

### 3. Implement Retry with Backoff

```javascript theme={null}
async function requestWithRetry(method, endpoint, data, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const result = await fyatuRequest(method, endpoint, data);

    if (result.success) return result;

    // Only retry on rate limits and server errors
    if (result.status === 429 || result.status >= 500) {
      const waitMs = Math.pow(2, attempt) * 1000;
      console.log(`Retry ${attempt}/${maxRetries} in ${waitMs}ms...`);
      await new Promise(r => setTimeout(r, waitMs));
      continue;
    }

    // Don't retry client errors (400, 401, 403, 404, 409)
    return result;
  }

  throw new Error(`Failed after ${maxRetries} retries`);
}
```

### 4. Log Errors for Debugging

```javascript theme={null}
function logApiError(endpoint, result) {
  console.error({
    timestamp: new Date().toISOString(),
    endpoint,
    status: result.status,
    code: result.error?.code,
    message: result.message,
    requestId: result.meta?.requestId,
    // Never log tokens, card numbers, or secrets
  });
}
```

## Idempotency

For operations that create resources, use the `externalReference` field to prevent duplicates:

```javascript theme={null}
// Use a deterministic reference for each operation
const reference = `order_${orderId}_${customerId}`;

const result = await fyatuRequest('POST', '/v3/collections', {
  amount: 25.00,
  currency: 'USD',
  externalReference: reference,
  description: 'Premium subscription'
});

// If you retry with the same externalReference,
// the API returns 409 instead of creating a duplicate
```

<Tip>
  This prevents duplicate charges if your request times out and you retry.
</Tip>

## Rate Limits

| Endpoint Category                 | Rate Limit              |
| --------------------------------- | ----------------------- |
| Authentication (`/v3/auth/*`)     | 10 requests per minute  |
| Read endpoints (GET)              | 120 requests per minute |
| Write endpoints (POST/PUT/DELETE) | 60 requests per minute  |

<Warning>
  Exceeding rate limits will result in `429 Too Many Requests`. Implement exponential backoff in your retry logic.
</Warning>
