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

# Error handling

> Understand and handle API errors

Rinne uses standard HTTP status codes and provides detailed error responses to help you debug issues quickly.

## Error response format

All errors follow a consistent structure:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation error",
    "status": 400,
    "path": "/v1/transactions",
    "timestamp": "2025-01-21T10:30:00.000Z",
    "requestId": "req_123456789",
    "details": {
      "issues": [
        {
          "field": "amount",
          "type": "REQUIRED",
          "message": "Field 'amount' is required"
        }
      ]
    }
  }
}
```

### Error fields

* `code`: Machine-readable error code
* `message`: Human-readable error message
* `status`: HTTP status code
* `path`: API endpoint that generated the error
* `timestamp`: When the error occurred
* `requestId`: Unique request identifier for support
* `details`: Additional error-specific information

## HTTP status codes

| Code  | Description           | Common causes                     |
| ----- | --------------------- | --------------------------------- |
| `400` | Bad Request           | Invalid input, validation errors  |
| `401` | Unauthorized          | Missing or invalid API key        |
| `403` | Forbidden             | Insufficient permissions          |
| `404` | Not Found             | Resource doesn't exist            |
| `409` | Conflict              | Duplicate resource, invalid state |
| `500` | Internal Server Error | Server-side error                 |
| `502` | Bad Gateway           | Provider integration error        |

## Common error codes

### VALIDATION\_ERROR (400)

Input validation failed. Check the `details.issues` array for specific field errors.

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation error",
    "details": {
      "issues": [
        {
          "field": "amount",
          "type": "INVALID_TYPE",
          "message": "Expected number, received string",
          "value": "invalid"
        }
      ]
    }
  }
}
```

**Solution**: Fix the invalid fields and retry the request.

<Note>
  Card credential fields (`card_data.number`, `card_data.cvv`, `card_data.network_token`, `card_data.cryptogram`, and the 3DS `card.number`) are accepted only as encrypted values from rinne-js. A plaintext value triggers a per-field `VALIDATION_ERROR` issue telling you to send the encrypted value from rinne-js instead. The rejected value is never echoed back in the error response.
</Note>

### AUTHENTICATION\_ERROR (401)

API key is missing or invalid.

```json theme={null}
{
  "error": {
    "code": "AUTHENTICATION_ERROR",
    "message": "Invalid or missing API key",
    "details": {
      "reason": "API key not found"
    }
  }
}
```

**Solution**: Verify your API key is correct and included in the `x-api-key` header.

### AUTHORIZATION\_ERROR (403)

You don't have permission to access the resource.

```json theme={null}
{
  "error": {
    "code": "AUTHORIZATION_ERROR",
    "message": "You need 'transaction.create' permission to access this resource"
  }
}
```

**Solution**: Check your user roles and permissions, or use an API key with appropriate access.

### RESOURCE\_NOT\_FOUND (404)

The requested resource doesn't exist.

```json theme={null}
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Transaction with ID 'tx_123' not found"
  }
}
```

**Solution**: Verify the resource ID is correct and belongs to your company.

### CONFLICT\_ERROR (409)

Resource already exists or operation conflicts with current state.

```json theme={null}
{
  "error": {
    "code": "CONFLICT_ERROR",
    "message": "Company with this document_number already exists",
    "details": {
      "field": "document_number",
      "value": "16525269000121"
    }
  }
}
```

**Solution**: Check for existing resources or verify the resource state allows the operation.

### INTEGRATION\_ERROR (502)

Provider integration failed.

```json theme={null}
{
  "error": {
    "code": "INTEGRATION_ERROR",
    "message": "Integration error with provider CELCOIN",
    "status": 502
  }
}
```

**Solution**: Retry the request. If the error persists, contact support with the `requestId`.

## Handling validation errors

Validation errors include detailed information about each invalid field:

```javascript theme={null}
try {
  const response = await createTransaction(data);
} catch (error) {
  if (error.status === 400 && error.code === 'VALIDATION_ERROR') {
    error.details.issues.forEach(issue => {
      console.log(`Field ${issue.field}: ${issue.message}`);
      // Display error to user
    });
  }
}
```

## Idempotency

Use the `request_id` field to safely retry requests:

```bash theme={null}
# First attempt - network error
POST /v1/transactions
{ "request_id": "order-123", ... }
# Network timeout

# Retry with same request_id
POST /v1/transactions
{ "request_id": "order-123", ... }
# Returns existing transaction if first request succeeded
```

This prevents duplicate transactions when retrying failed requests.

## Rate limiting

Rinne applies request rate limits to keep the platform stable: 1000 req/s for transaction creation and 3DS session creation and authentication `POST` requests and 50 req/s for all other requests. Each limit is a shared pool across all matching endpoints, not a per-endpoint allowance. Requests within the limit always succeed; only the excess requests above the limit are rejected, with no account block or penalty. See the [Rate limits guide](/guides/rate-limits) for full details.

Rejected requests return a `429 Too Many Requests` status code with **no response body**. The limit details are returned in the response headers instead:

```http theme={null}
HTTP/1.1 429 Too Many Requests
x-ratelimited: true
x-ratelimit-limit: 50, 50;w=1
x-ratelimit-remaining: 0
x-ratelimit-reset: 1
```

Detect a rate limit rejection from the `429` status code and the `x-ratelimit-*` headers, not from a response body. Implement exponential backoff when retrying:

```javascript theme={null}
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}
```

## Logging and debugging

### Request IDs

Every response includes a `requestId` that you can use when contacting support:

```json theme={null}
{
  "error": {
    "requestId": "req_123456789"
  }
}
```

Save request IDs for failed requests to help support diagnose issues.

### Error monitoring

Implement error monitoring to track API errors:

```javascript theme={null}
app.use((error, req, res, next) => {
  // Log to monitoring service
  logger.error('Rinne API error', {
    code: error.code,
    status: error.status,
    requestId: error.requestId,
    path: req.path
  });
  
  // Send to error tracking (Sentry, etc.)
  Sentry.captureException(error);
});
```

## Next steps

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Handle webhook delivery failures
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    View all error response schemas
  </Card>
</CardGroup>
