## Integration Guide

This guide covers best practices for integrating the Afriex Business API into your application.

[**OpenAPI spec**  
Get the full OpenAPI spec at this endpoint for code generation, SDKs, and API tooling.](https://api.afriex.com/api/docs.json)

[**MCP server**  
Add the Afriex Business API to your MCP client and search the docs from Cursor, Claude Code, and other MCP-enabled tools.](https://docs.afriex.com/mcp/introduction)

## Environments

The Afriex API provides two environments:

| Environment | Base URL | Purpose |
| --- | --- | --- |
| Staging | `https://sandbox.api.afriex.com` | Testing and development |
| Production | `https://api.afriex.com` | Live transactions |

**Never use production API keys in your testing environment.**

## Authentication

All API requests require authentication using your API key in the `x-api-key` header:

```bash
curl -H "x-api-key: YOUR_API_KEY" \
  https://sandbox.api.afriex.com/api/v1/customer
```

## Idempotency

For critical operations like creating transactions, use idempotency keys to prevent duplicate processing:

```json
{
  "customerId": "CUSTOMER_ID",
  "type": "WITHDRAW",
  "sourceAmount": "10",
  "destinationAmount": 5000,
  "sourceCurrency": "USD",
  "destinationCurrency": "NGN",
  "destinationId": "PAYMENT_METHOD_ID",
  "meta": {
    "idempotencyKey": "unique-key-123",
    "reference": "YOUR_REFERENCE"
  }
}
```

## Error Handling

The API uses standard HTTP status codes:

| Status | Meaning |
| --- | --- |
| 200 | Success |
| 201 | Created |
| 204 | No Content - Successful deletion, no body returned |
| 400 | Bad Request - Check your request payload |
| 401 | Unauthorized - Invalid or missing API key |
| 404 | Not Found - Resource doesn’t exist |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Server Error - Contact support |

### Error response format

All error responses follow a consistent structure:

```json
{
  "code": "INVALID_BUSINESS_CUSTOMER_REQUEST",
  "error": "Bad Request",
  "details": {
    "errorMessage": "PHONE_COUNTRY_MISMATCH",
    "friendlyMessage": "The phone number does not match the customer's country."
  }
}
```

| Field | Description |
| --- | --- |
| `code` | Machine-readable error code for programmatic handling |
| `error` | Short human-readable error category |
| `details.errorMessage` | Specific reason for the error |
| `details.friendlyMessage` | User-friendly message suitable for displaying to end users |

Use the `details.friendlyMessage` field to surface errors directly to your users, and use `code` for programmatic error handling in your application logic.

## Pagination

List endpoints support pagination with `page` and `limit` parameters:

```bash
curl -H "x-api-key: YOUR_API_KEY" \
  "https://sandbox.api.afriex.com/api/v1/customer?page=0&limit=10"
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `page` | integer | `0` | Page number, zero-indexed (first page = `0`) |
| `limit` | integer | `10` | Number of results per page (max `100`) |

All list endpoints return results in this shape:

```json
{
  "page": 0,
  "total": 20,
  "data": []
}
```

Use the `total` field to calculate the number of pages: `Math.ceil(total / limit)`.

## Webhook Integration

Set up webhooks to receive real-time event notifications:

1. Configure your webhook URL in the [Dashboard](https://business.afriex.com/)
2. Verify the `x-webhook-signature` header (`RSA-SHA256`, base64) using the **raw** request body
3. Use the webhook public key from **Dashboard -> Developers -> Webhooks** (staging and production keys are different)
4. Return `200` quickly after successful verification and processing

If signature verification fails, return `400` or `401`. Afriex retries failed deliveries up to 12 times with exponential backoff (starting at 30 seconds).

[**Webhook Documentation**  
Learn more about webhook setup, security, and event types.](https://docs.afriex.com/api-reference/endpoint/webhooks/introduction)

## Rate Limiting

Be mindful of rate limits when making API calls:

- Implement exponential backoff for retries
- Cache exchange rates and other static data
- Use webhooks instead of polling for status updates

## Testing Your Integration

1. Test in Staging
   
   Use the staging environment to test all API calls without affecting live data.
2. Verify Webhooks
   
   Test webhook handling with the staging environment events.
3. Go Live
   
   Switch to production base URL and API keys when ready.

### Testing transaction outcomes in sandbox

In the sandbox/staging environment, transactions reach a final status automatically, usually within 1-2 minutes of creation, so you can build and test your integration end to end. The matching `TRANSACTION.UPDATED` webhook is sent when the transaction settles. You can choose the outcome through the transaction’s `meta.reference`:

- If `meta.reference` contains the word `fail` (case-insensitive), the transaction settles as **FAILED**. For withdrawals, the amount is returned to your wallet.
- Otherwise, the transaction settles as **SUCCESS**.

Use this to test both your success and failure handling. This applies to the sandbox/staging environment only.

### Simulating OTP-required deposits

Some deposits (for example, certain mobile-money payments) require the customer to confirm a one-time password (OTP) before they complete. In sandbox you can trigger this flow deterministically through the deposit’s `meta.reference`:

- Include `SIMULATE_OTP` in `meta.reference` to make the deposit return the `CUSTOMER_ACTION_REQUIRED` status, meaning an OTP is needed.
- Include `SIMULATE_NO_OTP` to make the deposit complete without an OTP step.

When OTP is required, complete the deposit by calling [Authorize Transaction](https://docs.afriex.com/api-reference/endpoint/transactions/authorize) with the sandbox OTP `123456`:

```bash
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"OTP","otp":"123456"}' \
  https://sandbox.api.afriex.com/api/v1/transaction/TRANSACTION_ID/authorize
```

Any OTP other than `123456` is rejected, so you can test the wrong-OTP path. Once authorized, the deposit settles automatically like any other sandbox transaction (a `meta.reference` that also contains `fail` settles as **FAILED**, otherwise **SUCCESS**). These values apply to the sandbox/staging environment only; production ignores them.

## Support

Need help with your integration? Contact us at [support@afriex.com](mailto:support@afriex.com).
