## Customer

| Field         | Type                               | Description                                                  |
| --------------| ---------------------------------- | ------------------------------------------------------------ |
| `customerId` | string                             | Unique identifier for the customer                            |
| `name`       | string                             | Full name of the customer                                     |
| `email`      | string (email)                    | Email address                                                |
| `phone`      | string                             | Phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164) (e.g. `+2348192837465`) |
| `countryCode`| string                             | ISO 3166-1 alpha-2 country code (e.g. `NG`)                 |
| `meta`       | object                             | Optional metadata you attach to the customer                 |
| `createdAt`  | string (ISO 8601)                 | Timestamp when the customer was created                       |
| `updatedAt`  | string (ISO 8601)                 | Timestamp of the last update                                   |

On create and update requests, supply the customer’s full name as `fullName`; responses return it as `name`. KYC details are submitted separately through the [Update KYC](https://docs.afriex.com/api-reference/endpoint/customers/update-kyc) endpoint.

## Transaction

| Field                     | Type                               | Description                                                  |
| --------------------------| ---------------------------------- | ------------------------------------------------------------ |
| `transactionId`          | string                             | Unique identifier for the transaction                         |
| `customerId`             | string                             | ID of the customer who owns this transaction                  |
| `sourceId`               | string                             | ID of the source payment method                                |
| `destinationId`          | string                             | ID of the destination payment method                           |
| `sourceAmount`           | string                             | Amount debited from the source wallet                          |
| `sourceCurrency`         | string                             | ISO 4217 currency code of the source wallet (e.g. `USD`)    |
| `destinationAmount`      | string                             | Amount credited to the destination                              |
| `destinationCurrency`    | string                             | ISO 4217 currency code of the destination (e.g. `NGN`)       |
| `fee`                    | string                             | Fee charged for this transaction, denominated in `sourceCurrency`. Reported separately from `sourceAmount`. Omitted when no fee applied. |
| `type`                   | string                             | Transaction type. See [Transaction Types](https://docs.afriex.com/api-reference/models#transaction-types) |
| `channel`                | string                             | Payment channel of the transaction. See [Transaction Channels](https://docs.afriex.com/api-reference/models#transaction-channels). |
| `status`                 | string                             | Current status. See [Transaction Statuses](https://docs.afriex.com/api-reference/models#transaction-statuses) |
| `merchantReference`      | string                             | Merchant-supplied reference (mirrors `meta.reference` from the create request) |
| `meta`                   | object                             | Metadata attached to the transaction. See [TransactionMeta](https://docs.afriex.com/api-reference/models#transactionmeta) |
| `createdAt`              | string (ISO 8601)                 | Timestamp when the transaction was created                    |
| `updatedAt`              | string (ISO 8601)                 | Timestamp of the last update                                   |

### Transaction Types

| Value      | Description                                                                                                |
| ---------- | ---------------------------------------------------------------------------------------------------------- |
| `WITHDRAW`| Send funds from the Afriex wallet to a recipient’s payment method                                          |
| `DEPOSIT` | Receive funds into the Afriex wallet                                                                        |
| `SWAP`    | Convert funds between currencies within the Afriex wallet                                                  |

### Transaction Channels

| Channel                 | Description                                                                                               |
| ---------------------- | ---------------------------------------------------------------------------------------------------------- |
| `BANK_ACCOUNT`        | Local bank account transfer                                                                                 |
| `SWIFT`               | International SWIFT wire transfer                                                                          |
| `MOBILE_MONEY`        | Mobile money wallet (e.g. M-Pesa, MTN MoMo)                                                               |
| `UPI`                 | Unified Payments Interface (India)                                                                         |
| `INTERAC`             | Interac e-Transfer (Canada)                                                                                |
| `WE_CHAT`            | WeChat Pay                                                                                                 |
| `CARD`                | Card payment                                                                                               |
| `CRYPTO`              | Crypto rail                                                                                                 |
| `VIRTUAL_BANK_ACCOUNT` | Hosted virtual bank account                                                                                 |
| `POOL_ACCOUNT`        | Shared pool account for incoming deposits                                                                  |
| `ACH_BANK_ACCOUNT`    | US ACH bank transfer                                                                                        |
| `PAYBILL_TILL`       | Paybill / Till (M-Pesa business numbers)                                                                   |
| `RFP`                 | Request for Payment                                                                                         |
| `VIRTUAL_CARD`       | Virtual card                                                                                                |
| `ALIPAY`              | Alipay                                                                                                      |
| `WALLET`              | Afriex wallet (internal leg)                                                                                |

### Transaction Statuses

| Status                      | Description                                                                                               |
| ----------------------------| ---------------------------------------------------------------------------------------------------------- |
| `PENDING`                  | Transaction has been received and is awaiting processing                                                  |
| `PROCESSING`               | Transaction is actively being processed                                                                    |
| `SUCCESS`                  | Transaction settled successfully                                                                            |
| `FAILED`                   | Transaction failed. Check `meta.failureReason` for the reason code. See [Failure reasons](https://docs.afriex.com/api-reference/models#failure-reasons). |
| `CANCELLED`                | Transaction was cancelled before processing                                                                  |
| `REFUNDED`                 | Funds were returned to the sender                                                                           |
| `RETRY`                    | Transaction is being automatically retried                                                                  |
| `UNKNOWN`                  | Status could not be determined. Contact support.                                                          |
| `SCHEDULED`                | Transaction is scheduled to be processed later                                                             |
| `CUSTOMER_ACTION_REQUIRED` | Transaction is paused pending an action from the customer                                                  |
| `REJECTED`                 | Transaction was rejected after review                                                                       |
| `IN_REVIEW`                | Transaction is under manual review                                                                          |
| `DISPUTED`                 | A dispute has been raised against the transaction                                                           |
| `DISPUTE_RESOLVED`        | The dispute has been resolved                                                                                |
| `DISPUTE_WON`             | The dispute was resolved in your favour                                                                     |
| `DISPUTE_LOST`            | The dispute was resolved against you                                                                         |
| `DISPUTE_EVIDENCE_SUBMITTED`| Evidence has been submitted for the dispute                                                                |

### TransactionMeta

The `meta` object passed when creating a transaction. `idempotencyKey` and `reference` are required.

| Field               | Type      | Required | Description                                              |
| --------------------| --------- | -------- | -------------------------------------------------------- |
| `idempotencyKey`   | string    | **Yes** | Unique key to prevent duplicate transactions. Use a UUID or your own unique identifier. |
| `reference`        | string    | **Yes** | Your internal reference for the transaction (e.g. order ID). Returned on the transaction as `merchantReference`. |
| `narration`        | string    | No      | Human-readable reason or description for the transaction |
| `invoice`          | string    | No      | Base64-encoded invoice document to attach to the transaction. |
| `settlement`       | string    | No      | Settlement handling. One of `spot` (default, processes immediately) or `request` (WITHDRAW only, queues the payout for the next business day in the destination payment method’s country). `scheduled` is derived server-side and should not be sent. |

```json
{
  "meta": {
    "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
    "reference": "ORDER-12345",
    "narration": "Payment for invoice #12345",
    "invoice": "base64EncodedStringHere"
  }
}
```

Always provide a unique `idempotencyKey` per transaction attempt. If you retry a failed request with the same key, Afriex will return the original transaction instead of creating a duplicate.

### Server-set meta fields

Afriex also sets fields on `meta` after the transaction is created. These echo alongside any fields you passed on create.

| Field             | Type     | Description                                                                                     |
| ------------------| ---------| ----------------------------------------------------------------------------------------------- |
| `otpRequired`     | boolean  | `true` when the customer must complete an OTP challenge before the transaction can settle. See [Simulating OTP-required deposits](https://docs.afriex.com/development#simulating-otp-required-deposits). |
| `failureReason`   | object   | Present only when `status` is `FAILED` or `REJECTED`. Carries a stable `code`, a customer-safe `message`, and a `retryable` flag. See [Failure reasons](https://docs.afriex.com/api-reference/models#failure-reasons). |

### Failure reasons

When a transaction reaches `FAILED` or `REJECTED`, `meta.failureReason` explains why using a stable `AFX_*` code and a customer-safe message. Branch on `code` rather than the underlying rail so your integration stays stable across routing changes.

| Field               | Type    | Description                                                                                                   |
| --------------------| ------- | ------------------------------------------------------------------------------------------------------------- |
| `code`              | string  | Stable `AFX_*` failure code. Safe to switch on; the set grows over time but existing values do not change meaning. |
| `message`           | string  | Customer-safe short description of the failure. Suitable for display; do not parse. Branch on `code` instead. |
| `retryable`         | boolean | `true` when re-submitting the same request (or, for `AFX_PROPOSAL_EXPIRED`, starting a fresh proposal) may succeed. `false` when the caller must change the request before retrying. |

```json
{
  "meta": {
    "failureReason": {
      "code": "AFX_VELOCITY_LIMIT_EXCEEDED",
      "message": "This transaction exceeds the allowed velocity limit. Try again later.",
      "retryable": true
    }
  }
}
```

| Code                             | Meaning                                                                                     |
| -------------------------------- | ------------------------------------------------------------------------------------------- |
| `AFX_REQUEST_FAILED`             | Generic failure while processing the request. Usually retryable.                          |
| `AFX_SYSTEM_ERROR`               | Internal Afriex error. Retry with backoff.                                               |
| `AFX_SERVICE_UNAVAILABLE`        | A downstream service was unavailable. Retry with backoff.                                |
| `AFX_INVALID_CURRENCY`           | The requested currency is not supported for this route.                                   |
| `AFX_INVALID_AMOUNT`             | The requested amount is invalid or outside allowed limits.                                  |
| `AFX_INVALID_RECIPIENT`          | The recipient payment method is invalid or incomplete.                                     |
| `AFX_RECIPIENT_NOT_FOUND`        | The recipient account could not be located at the destination institution.                 |
| `AFX_BENEFICIARY_RESTRICTED`     | The beneficiary is restricted and cannot receive funds.                                   |
| `AFX_INVALID_SENDER`             | The sender’s payment method is invalid or ineligible.                                     |
| `AFX_INVALID_REQUEST`            | The request payload is malformed or inconsistent.                                          |
| `AFX_VELOCITY_LIMIT_EXCEEDED`   | The customer has exceeded a velocity limit. Retry after the window resets.                 |
| `AFX_AMOUNT_LIMIT_EXCEEDED`     | The request exceeds a per-transaction, daily, or account limit.                           |
| `AFX_PAYMENT_FAILED`             | The payment leg failed at the payment rail.                                              |
| `AFX_COMPLIANCE_REJECTED`       | The transaction was rejected during compliance review.                                     |
| `AFX_PROPOSAL_EXPIRED`          | The rate proposal expired before the transaction was committed. Start a fresh proposal.   |

The `AFX_*` set may grow over time as new failure modes are surfaced. Existing values will not change meaning. Handle unknown codes by falling back to `message`.

## PaymentMethod

Empty fields are omitted from responses. Account-shaped channels (`BANK_ACCOUNT`, `MOBILE_MONEY`, `SWIFT`, etc.) populate `accountName`, `accountNumber`, and `institution`; the `CARD` channel instead populates `last4`, `brand`, `expiration`, and `cardName`.

| Field                  | Type                               | Description                                                  |
| -----------------------| ---------------------------------- | ------------------------------------------------------------ |
| `paymentMethodId`     | string                             | Unique identifier for the payment method                     |
| `customerId`          | string                             | ID of the customer who owns this payment method               |
| `channel`             | string                             | Payment channel. See [Payment Channels](https://docs.afriex.com/api-reference/models#payment-channels) |
| `countryCode`         | string                             | ISO 3166-1 alpha-2 country code                             |
| `currency`            | string                             | ISO 4217 currency code. Use this instead of inferring from `countryCode`, which is ambiguous (e.g. a USD account in a non-US country) |
| `capabilities`        | string[]                           | Operations this method is enabled for (e.g. `DEPOSIT`, `WITHDRAW`). Filterable via the `capabilities` query parameter on [List Payment Methods](https://docs.afriex.com/api-reference/endpoint/payment-methods/list) |
| `reference`           | string                             | Identifier used to reconcile incoming deposits against        |
| `accountName`         | string                             | Account holder name. Present for account-shaped channels     |
| `accountNumber`       | string                             | Account number. Present for account-shaped channels          |
| `institution`         | object                             | Bank or provider details. See [PaymentMethodInstitution](https://docs.afriex.com/api-reference/models#paymentmethodinstitution) |
| `recipient`           | object                             | Recipient contact details. See [PaymentMethodRecipient](https://docs.afriex.com/api-reference/models#paymentmethodrecipient) |
| `transaction`         | object                             | Per-transaction overrides. See [PaymentMethodTransaction](https://docs.afriex.com/api-reference/models#paymentmethodtransaction) |
| `status`              | string                             | Lifecycle status. See [Payment Method Statuses](https://docs.afriex.com/api-reference/models#payment-method-statuses) |
| `last4`               | string                             | Last 4 digits of the card. `CARD` channel only              |
| `brand`               | string                             | Card brand (e.g. `Visa`, `MasterCard`). `CARD` channel only |
| `expiration`          | object                             | Card expiry with `month` and `year`. `CARD` channel only    |
| `cardName`            | string                             | Name on the card. `CARD` channel only                       |
| `expiresInMinutes`    | integer                            | Minutes until a dynamic virtual account expires, when applicable |
| `amount`              | number                             | Requested amount for a dynamic virtual account, when applicable |
| `extra`               | object                             | Additional channel-specific properties                       |
| `meta`                | object                             | Optional metadata attached to the payment method            |

### Payment Channels

| Channel                | Description                                                                                          |
| ----------------------| -----------------------------------------------------------------------------------------------------|
| `BANK_ACCOUNT`       | Local bank account transfer                                                                             |
| `MOBILE_MONEY`       | Mobile money wallet (e.g. M-Pesa, MTN MoMo)                                                           |
| `SWIFT`              | International SWIFT wire transfer                                                                       |
| `INTERAC`            | Interac e-Transfer (Canada)                                                                             |
| `UPI`                | Unified Payments Interface (India)                                                                       |
| `WE_CHAT`           | WeChat Pay                                                                                              |
| `ALIPAY`             | Alipay                                                                                                   |
| `CARD`               | Card payment                                                                                             |
| `CRYPTO`             | Crypto rail                                                                                             |
| `VIRTUAL_BANK_ACCOUNT`| Hosted virtual bank account                                                                               |
| `POOL_ACCOUNT`       | Shared pool account for incoming deposits                                                               |
| `ACH_BANK_ACCOUNT`   | US ACH bank transfer                                                                                   |
| `PAYBILL_TILL`      | Paybill / Till (M-Pesa business numbers)                                                                 |
| `RFP`                | Request for Payment                                                                                     |
| `VIRTUAL_CARD`      | Virtual card                                                                                             |

Not every channel is valid when **creating** a payment method. The [Create Payment Method](https://docs.afriex.com/api-reference/endpoint/payment-methods/create) endpoint accepts a narrower set of channels; the list above covers all channels that can appear on a returned payment method.

### Payment Method Statuses

| Status    | Description                                                         |
| --------- | ------------------------------------------------------------------- |
| `active`  | Payment method is active and usable                                 |
| `pending` | Payment method is awaiting activation or verification                 |
| `deleted` | Payment method has been deleted                                     |
| `expired` | Payment method has expired (e.g. an expired card)                  |
| `blocked` | Payment method has been blocked                                      |

## PaymentMethodRecipient

Recipient contact details attached to a payment method, when supplied.

| Field              | Type    | Description                                                  |
| -------------------| ------- | ---------------------------------------------------------- |
| `recipientName`   | string  | Name of the recipient                                       |
| `recipientEmail`  | string (email) | Email address of the recipient                           |
| `recipientPhone`  | string  | Phone number of the recipient in E.164 format            |
| `recipientAddress` | string  | Address of the recipient                                   |

## PaymentMethodTransaction

Optional per-transaction details attached to a payment method.

| Field                 | Type    | Description                                                  |
| ----------------------| ------- | ---------------------------------------------------------- |
| `transactionNarration`| string  | Optional reason or description for the transaction         |
| `transactionInvoice`  | string  | Base64-encoded invoice for the transaction                 |

## PaymentMethodInstitution

| Field                  | Type    | Description                                                  |
| -----------------------| ------- | ---------------------------------------------------------- |
| `institutionId`       | string  | Unique ID of the bank or mobile money provider             |
| `institutionName`     | string  | Display name of the institution                             |
| `institutionCode`     | string  | Institution code (e.g. bank code)                         |
| `institutionAddress`   | string  | Physical address of the institution, if required          |

Use the [List Institutions](https://docs.afriex.com/api-reference/endpoint/payment-methods/institution) endpoint to retrieve valid institutions for a given country and channel.

## Error Response

All API errors return a consistent JSON body:

| Field                      | Type    | Description                                                  |
| ---------------------------| ------- | ---------------------------------------------------------- |
| `code`                     | string  | Machine-readable error code (e.g. `AUTHENTICATION_ERROR`) |
| `error`                    | string  | Short error category (e.g. `Bad Request`)                  |
| `details.errorMessage`    | string  | Specific error reason (e.g. `PHONE_COUNTRY_MISMATCH`)     |
| `details.friendlyMessage`  | string  | User-friendly message you can display to end users        |
| `details.data`            | object  | Optional caller-safe context. On a customer-create uniqueness conflict (`EMAIL_ALREADY_EXISTS`, `PHONE_NUMBER_ALREADY_EXISTS`) contains `{ customerId }` for the existing customer so you can adopt it without a follow-up lookup |

```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."
  }
}
```

### Common error codes

| Code                            | HTTP Status | Description                                                      |
| -------------------------------| -----------| --------------------------------------------------------------- |
| `AUTHENTICATION_ERROR`        | 401        | API key is missing or invalid                                   |
| `INVALID_BUSINESS_CUSTOMER_REQUEST` | 400         | Customer request validation failed                             |
| `PHONE_COUNTRY_MISMATCH`      | 400         | Phone number country does not match `countryCode`            |
| `EMAIL_ALREADY_EXISTS`        | 409        | A customer with this email already exists. `details.data.customerId` returns the existing customer’s id. |
| `PHONE_NUMBER_ALREADY_EXISTS`  | 409        | A customer with this phone number already exists. `details.data.customerId` returns the existing customer’s id. |
| `NOT_FOUND`                    | 404        | The requested resource does not exist                          |
| `RATE_LIMIT_EXCEEDED`        | 429        | Too many requests. Slow down and retry with backoff.          |
