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

# Error Codes

> Complete reference of API error codes and how to handle them

# Error Codes

All errors return a consistent JSON structure with a machine-readable `code` field for programmatic handling and a human-readable `message` field for debugging.

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "message": "Amount must be between 0.01 and 10000",
    "param": "amount",
    "code": "amount_out_of_range"
  }
}
```

## Error Types

| Type                    | Description                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| `invalid_request_error` | The request was malformed or missing required parameters.                                                   |
| `authentication_error`  | Invalid, expired, or missing API key.                                                                       |
| `authorization_error`   | Valid API key but insufficient permissions for the requested resource.                                      |
| `not_found_error`       | The requested resource does not exist.                                                                      |
| `conflict_error`        | The request conflicts with existing state (e.g., duplicate idempotency key, cancelling a confirmed intent). |
| `rate_limit_error`      | Too many requests. Back off and retry.                                                                      |
| `api_error`             | An internal error on our side. Retry with exponential backoff.                                              |

## Payment Intent Errors

| Code                       | HTTP Status | Description                                                   | Resolution                                                    |
| -------------------------- | ----------- | ------------------------------------------------------------- | ------------------------------------------------------------- |
| `amount_out_of_range`      | 400         | Amount is below $0.01 or above $10,000.                       | Adjust the amount to within the allowed range.                |
| `invalid_currency`         | 400         | Currency is not `usd`.                                        | Set currency to `"usd"`.                                      |
| `invalid_merchant`         | 400         | Merchant ID is malformed.                                     | Check the merchant ID format (`mer_*`).                       |
| `merchant_not_found`       | 404         | Merchant does not exist or is not accessible to this API key. | Verify the merchant ID and API key permissions.               |
| `merchant_not_active`      | 422         | Merchant account is suspended or deactivated.                 | Contact support or check merchant status in the dashboard.    |
| `intent_not_cancellable`   | 409         | Payment intent is not in `awaiting_payment` status.           | Only intents with status `awaiting_payment` can be cancelled. |
| `idempotency_key_conflict` | 409         | The idempotency key was used with different parameters.       | Use a unique idempotency key for each distinct request.       |
| `ttl_out_of_range`         | 400         | TTL is below 60 or above 3600 seconds.                        | Set TTL between 60 and 3600.                                  |

## Authentication Errors

| Code                   | HTTP Status | Description                                                  | Resolution                                                  |
| ---------------------- | ----------- | ------------------------------------------------------------ | ----------------------------------------------------------- |
| `missing_api_key`      | 401         | No `Authorization` header provided.                          | Include `Authorization: Bearer sk_*` in request headers.    |
| `invalid_api_key`      | 401         | API key is malformed or does not exist.                      | Check that the key is correct and hasn't been revoked.      |
| `revoked_api_key`      | 401         | API key has been revoked.                                    | Generate a new API key from the dashboard.                  |
| `environment_mismatch` | 403         | Using a test key against production resources or vice versa. | Use `sk_test_*` for sandbox and `sk_live_*` for production. |

## Webhook Errors

| Code                  | HTTP Status | Description                                          | Resolution                                                   |
| --------------------- | ----------- | ---------------------------------------------------- | ------------------------------------------------------------ |
| `invalid_webhook_url` | 400         | Webhook URL is not a valid HTTPS URL.                | Provide a valid HTTPS URL.                                   |
| `webhook_unreachable` | 422         | Could not reach the webhook URL during registration. | Ensure your endpoint is publicly accessible and returns 200. |

## Rate Limit Errors

| Code                  | HTTP Status | Description                              | Resolution                                                                   |
| --------------------- | ----------- | ---------------------------------------- | ---------------------------------------------------------------------------- |
| `rate_limit_exceeded` | 429         | Too many requests in the current window. | Wait and retry. Check `X-RateLimit-Reset` header for when the window resets. |

## Handling Errors

<CodeGroup>
  ```javascript Node.js theme={null}
  async function createPaymentIntent(amount, merchantId) {
    const response = await fetch('https://api.stablegenius.co/v1/payment-intents', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ amount, currency: 'usd', merchant_id: merchantId }),
    });

    if (!response.ok) {
      const { error } = await response.json();

      switch (error.type) {
        case 'authentication_error':
          // API key issue — check configuration
          throw new Error(`Auth failed: ${error.message}`);
        case 'rate_limit_error':
          // Back off and retry
          const resetAt = response.headers.get('X-RateLimit-Reset');
          await sleep(resetAt * 1000 - Date.now());
          return createPaymentIntent(amount, merchantId); // Retry
        case 'api_error':
          // Our issue — retry with backoff
          await sleep(1000);
          return createPaymentIntent(amount, merchantId);
        default:
          throw new Error(`API error: ${error.code} — ${error.message}`);
      }
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  import requests
  import time

  def create_payment_intent(amount, merchant_id):
      response = requests.post(
          'https://api.stablegenius.co/v1/payment-intents',
          headers={
              'Authorization': f'Bearer {API_KEY}',
              'Content-Type': 'application/json',
          },
          json={'amount': amount, 'currency': 'usd', 'merchant_id': merchant_id},
      )

      if not response.ok:
          error = response.json().get('error', {})
          error_type = error.get('type')

          if error_type == 'rate_limit_error':
              reset_at = int(response.headers.get('X-RateLimit-Reset', 0))
              time.sleep(max(reset_at - time.time(), 1))
              return create_payment_intent(amount, merchant_id)

          if error_type == 'api_error':
              time.sleep(1)
              return create_payment_intent(amount, merchant_id)

          raise Exception(f"API error: {error.get('code')} — {error.get('message')}")

      return response.json()
  ```
</CodeGroup>

## Best Practices

* **Always check `error.code`** for programmatic handling, not `error.message` (messages may change).
* **Retry on `429` and `500`** with exponential backoff. Use the `X-RateLimit-Reset` header for rate limits.
* **Never retry on `400`, `401`, `403`, `404`** — these indicate issues with your request, not transient failures.
* **Log the full error response** for debugging. Include the `error.type`, `error.code`, and `error.param` fields.
