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

# Quote a Withdrawal

> Returns what a withdrawal would cost, priced by the same code that charges it, without creating anything. Type exactly one of `sourceAmount` (what you send) or `destinationAmount` (what the recipient receives); the other is derived from it. The side you type is returned as `anchor`.

`rate`, `fee` and the amounts mean exactly what they mean on the transaction a create returns, so a create with the same inputs charges the quoted amounts. A quote does not check limits or customer verification and does not need an idempotency key or reference; those apply when you create the transaction, so a quote can succeed where the create is refused. Quotes are not reserved: rates and promotions can change before you create.

Name the destination either with `destinationId` (a saved payment method) or with `destinationChannel` and `destinationCountry` (when the customer has no account to send to yet), not both. `destinationCountry` can be left out for `SWIFT` and `CRYPTO`. A quote priced from a channel alone comes back with `indicative: true`: the account is not known, so the processor that would carry the payout, and any fee that depends on it, may differ when you create. `customerId` is optional; without it the quote is priced for the business owner, so a fee or promotion that depends on the customer's country can differ when you create.


Price a withdrawal before you create it. A quote returns the rate, the fee, and both amounts, and it creates nothing, so you can show your customer exactly what a send will cost.

A quote is priced by the same logic that charges a transaction. If you [create the transaction](/api-reference/endpoint/transactions/create) with the same inputs, you are charged the quoted amounts.

<Note>
  Requires the `TRANSACTION.WITHDRAW.CREATE` key permission. See [Permissions](/api-reference/permissions).
</Note>

## How to use it

<Steps>
  <Step title="Choose the side you type">
    Send exactly one of `sourceAmount` (what you send) or `destinationAmount` (what the recipient receives). The API derives the other side. The response `anchor` tells you which side is exact: `source` or `destination`.
  </Step>

  <Step title="Name the destination">
    Send either `destinationId` (a saved payment method) or `destinationChannel` plus `destinationCountry` (when the customer has no account to send to yet), never both. `destinationCountry` can be left out for `SWIFT` and `CRYPTO`.
  </Step>

  <Step title="Optionally pass the customer">
    `customerId` is optional. Without it, the quote is priced for the business owner.
  </Step>
</Steps>

## Example

```bash theme={null}
curl -G "https://sandbox.api.afriex.com/api/v1/transaction/quote" \
  -H "x-api-key: YOUR_API_KEY" \
  --data-urlencode "sourceCurrency=NGN" \
  --data-urlencode "destinationCurrency=USD" \
  --data-urlencode "sourceAmount=100000" \
  --data-urlencode "destinationId=PAYMENT_METHOD_ID"
```

```json theme={null}
{
  "data": {
    "anchor": "source",
    "sourceAmount": "100000",
    "sourceCurrency": "NGN",
    "destinationAmount": "65.76696108",
    "destinationCurrency": "USD",
    "rate": "0.0006576696108"
  }
}
```

`rate` reads as `1 sourceCurrency = rate destinationCurrency`. `fee` is in `sourceCurrency` and is left out when no fee applies.

## Things to know

* **Quotes are not reserved.** Rates and promotions can change before you create, so quote again close to the send if the price matters.
* **A quote can succeed where the create is refused.** A quote does not check limits or customer verification, and it needs no idempotency key or reference. Those apply when you create the transaction.
* **`indicative: true`.** When you price from `destinationChannel` alone, the account is not known yet, so the payout route and any fee that depends on it can differ when you create. The response carries `indicative: true` to tell you so.
* **Pass `customerId` for an exact price.** A fee or promotion that depends on the customer's country can differ from a business-owner quote.

## Errors

A `422` means the request is invalid: you sent both amounts or neither, both `destinationId` and `destinationChannel` or neither, or a required parameter is missing.


## OpenAPI

````yaml GET /api/v1/transaction/quote
openapi: 3.1.0
info:
  title: Afriex Business API
  version: 1.0.13
  description: >-
    Welcome to the Afriex Business API. This API allows you to manage customers,

    process payments, handle payouts, and receive real-time notifications via

    webhooks.


    For detailed guidance on authentication, pagination, error handling, and

    webhooks, please refer to the [dedicated

    guides](https://docs.afriex.com) in the top bar. The guide

    provides a step-by-step instructions to help you integrate seamlessly.


    ## Testing in sandbox


    Send requests to `https://sandbox.api.afriex.com` with a sandbox API key.

    No real money moves. Deposits, payouts and virtual accounts can be tested

    on every payment method and currency your business supports.


    **When transactions complete.** A sandbox deposit or payout completes on

    its own about 5 minutes after it is created, and your webhook receives the

    final status, exactly as in production. To complete one sooner:


    - Include `SIMULATE_INSTANT` anywhere in `meta.reference` when you create
      it, and it completes in about 30 seconds.
    - Or call `POST /api/v1/transaction/{transactionId}/simulate` with
      `{"outcome": "success"}` or `{"outcome": "failed"}` to complete it now.

    **Choosing an outcome.** The last four digits of the account number, the

    phone number or the card number decide what happens. A payment method with

    none of these (an email or a wallet address) succeeds unless you complete it

    with `/simulate`:


    | Ends in | Payout | Mobile money deposit | Card deposit |

    | --- | --- | --- | --- |

    | anything else | Succeeds | Succeeds | Succeeds |

    | `0001` | Fails; your balance is refunded | Fails | Fails |

    | `0002` | Succeeds | Waits for a one-time password (see below) | Succeeds |

    | `0003` | Stays pending until you call `/simulate` | Stays pending until
    you call `/simulate` | Stays pending until you call `/simulate` |

    | `0004` | Account name lookup (`GET /api/v1/payment-method/resolve`) fails
    | Succeeds | Succeeds |

    | `0005` | Declined immediately | Declined immediately | Declined
    immediately |


    A phone number must still be a valid mobile number for its country, so keep

    the country prefix and change only the last digits, for example

    `+254712340002` in Kenya. Account name lookups return `Sandbox Test
    Account`.


    **One-time passwords.** A mobile money deposit from a wallet ending in

    `0002` returns `CUSTOMER_ACTION_REQUIRED`. Complete it with

    `POST /api/v1/transaction/{transactionId}/authorize` and the OTP `123456`;

    any other OTP returns `OTP_INCORRECT`. The deposit then completes as above.


    **Virtual accounts.** Sandbox virtual accounts receive no money on their

    own. Pay into one with

    `POST /api/v1/payment-method/virtual-account/simulate-transfer`.


    **Cards.** Collect the card as you would in production, using a test card

    number whose last four digits choose the outcome.


    **Balances and webhooks.** Fund your sandbox balance with

    `POST /api/v1/org/balance/topup`, and send yourself a sample webhook with

    `POST /api/v1/webhooks/trigger`.
  termsOfService: https://www.afriex.com/terms-and-condition
  contact:
    name: Afriex API Support
    email: support@afriex.com
    url: https://docs.afriex.com
  license:
    name: Proprietary
    url: https://www.afriex.com/terms-and-condition
servers:
  - url: https://sandbox.api.afriex.com
    description: Staging Base URL
  - url: https://api.afriex.com
    description: Production Base URL
security:
  - ApiKey: []
tags:
  - name: Customers
    description: Create and manage your customers.
  - name: Payment Methods
    description: Register and resolve customer payout and collection methods.
  - name: Transactions
    description: Create and track deposits, withdrawals, and swaps.
  - name: Balance
    description: View and top up your business wallet balances.
  - name: Rates
    description: Fetch real-time exchange rates.
  - name: Checkout Sessions
    description: Create hosted checkout sessions.
  - name: Webhooks
    description: Webhook event payloads and sandbox webhook testing.
  - name: Media
    description: Generate presigned URLs for secure file uploads.
  - name: Payment Batches
    description: >-
      Manage payment batches, their recipients and their runs.


      Where payload signing is enabled for your business, sign the JSON of the
      request body for POST and PATCH routes that take one. For GET routes with
      `page` and `limit`, and for the withdraw route, sign the query parameters
      as the server reads them, with defaults applied: a list call with no
      parameters signs `{"limit":100,"page":0}` and a withdraw with no
      `sessionId` signs `{}`. For the remaining routes (get, delete and
      remove-recipient) sign the path parameters, e.g. `{"batchId":"..."}`.
paths:
  /api/v1/transaction/quote:
    parameters:
      - $ref: '#/components/parameters/x-api-version'
    get:
      tags:
        - Transactions
      summary: Quote a withdrawal
      description: >
        Returns what a withdrawal would cost, priced by the same code that
        charges it, without creating anything. Type exactly one of
        `sourceAmount` (what you send) or `destinationAmount` (what the
        recipient receives); the other is derived from it. The side you type is
        returned as `anchor`.


        `rate`, `fee` and the amounts mean exactly what they mean on the
        transaction a create returns, so a create with the same inputs charges
        the quoted amounts. A quote does not check limits or customer
        verification and does not need an idempotency key or reference; those
        apply when you create the transaction, so a quote can succeed where the
        create is refused. Quotes are not reserved: rates and promotions can
        change before you create.


        Name the destination either with `destinationId` (a saved payment
        method) or with `destinationChannel` and `destinationCountry` (when the
        customer has no account to send to yet), not both. `destinationCountry`
        can be left out for `SWIFT` and `CRYPTO`. A quote priced from a channel
        alone comes back with `indicative: true`: the account is not known, so
        the processor that would carry the payout, and any fee that depends on
        it, may differ when you create. `customerId` is optional; without it the
        quote is priced for the business owner, so a fee or promotion that
        depends on the customer's country can differ when you create.
      operationId: getTransactionQuote
      parameters:
        - name: customerId
          in: query
          required: false
          description: >-
            The customer the withdrawal is for. Omit it to price the quote for
            the business owner.
          schema:
            type: string
        - name: destinationId
          in: query
          required: false
          description: >-
            The destination payment method the funds would be sent to. Send this
            or `destinationChannel`, not both.
          schema:
            type: string
        - name: destinationChannel
          in: query
          required: false
          description: >-
            The channel the funds would be sent over, when there is no saved
            payment method yet. Send this or `destinationId`, not both.
          schema:
            type: string
            examples:
              - BANK_ACCOUNT
        - name: destinationCountry
          in: query
          required: false
          description: >-
            The destination country (ISO 3166-1 alpha-2), required with
            `destinationChannel` except for `SWIFT` and `CRYPTO`.
          schema:
            type: string
            examples:
              - NG
        - name: sourceCurrency
          in: query
          required: true
          description: The currency debited from your wallet.
          schema:
            type: string
            examples:
              - NGN
        - name: destinationCurrency
          in: query
          required: true
          description: The currency the recipient receives.
          schema:
            type: string
            examples:
              - USD
        - name: sourceAmount
          in: query
          required: false
          description: The amount you send. Send this or `destinationAmount`, not both.
          schema:
            type: string
            examples:
              - '100000'
        - name: destinationAmount
          in: query
          required: false
          description: >-
            The amount the recipient receives. Send this or `sourceAmount`, not
            both.
          schema:
            type: string
            examples:
              - '65.77'
      responses:
        '200':
          description: The quote.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    required:
                      - anchor
                      - sourceCurrency
                      - destinationCurrency
                    properties:
                      anchor:
                        type: string
                        enum:
                          - source
                          - destination
                        description: >-
                          The side that is exact; the other side is derived from
                          it.
                      sourceAmount:
                        type: string
                        description: What is debited, before any fee.
                      sourceCurrency:
                        type: string
                      destinationAmount:
                        type: string
                        description: What the recipient receives.
                      destinationCurrency:
                        type: string
                      indicative:
                        type: boolean
                        description: >-
                          Present and `true` when the quote was priced from
                          `destinationChannel` alone, so the processor and any
                          fee that depends on it may differ at create.
                      rate:
                        type: string
                        description: >-
                          `1 sourceCurrency = rate destinationCurrency`, the
                          published rate after any promotion.
                      fee:
                        type: string
                        description: >-
                          The fee, in `sourceCurrency`. Omitted when no fee
                          applies.
              examples:
                typedSource:
                  summary: Typed the source amount
                  value:
                    data:
                      anchor: source
                      sourceAmount: '100000'
                      sourceCurrency: NGN
                      destinationAmount: '65.76696108'
                      destinationCurrency: USD
                      rate: '0.0006576696108'
                typedDestination:
                  summary: Typed the destination amount, with a fee
                  value:
                    data:
                      anchor: destination
                      sourceAmount: '100000'
                      sourceCurrency: NGN
                      destinationAmount: '65.76696108'
                      destinationCurrency: USD
                      rate: '0.0006576696108'
                      fee: '50'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingApiKey:
                  summary: Missing API key
                  value:
                    code: AUTHENTICATION_ERROR
                    error: Authorization header is missing
                    details: {}
        '422':
          description: >-
            The request is invalid: both amounts, or neither, were sent, both
            `destinationId` and `destinationChannel` (or neither) were sent, or
            a required parameter is missing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                bothAmounts:
                  summary: Both amounts sent
                  value:
                    code: VALIDATION_ERROR
                    error: >-
                      Failed to parse request. Issues: 'value' contains a
                      conflict between exclusive peers [sourceAmount,
                      destinationAmount]
                    details: {}
        '500':
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                serverError:
                  summary: Unexpected server error
                  value:
                    code: INTERNAL_SERVER_ERROR
                    error: It's not you, it's us, please reach out to support
                    details: {}
components:
  parameters:
    x-api-version:
      name: x-api-version
      in: header
      required: false
      description: >-
        API version in ISO 8601 format. The only supported version is
        `2026-05-18`, which is also the default when the header is omitted. Any
        other value is rejected with a `400 Bad Request`.
      schema:
        type: string
  schemas:
    ErrorResponse:
      type: object
      properties:
        code:
          type: string
          description: Machine-readable error code.
        error:
          type: string
          description: Human-readable error message.
        details:
          $ref: '#/components/schemas/ErrorDetails'
    ErrorDetails:
      type: object
      properties:
        errorMessage:
          type: string
          description: Detailed/technical error message.
        friendlyMessage:
          type: string
          description: User-facing error message safe to display.
        data:
          type: object
          description: >-
            Optional caller-safe context for the error. On a customer-create
            uniqueness conflict (EMAIL_ALREADY_EXISTS /
            PHONE_NUMBER_ALREADY_EXISTS) this carries the existing customer's
            id, so you can adopt it without a follow-up lookup.
          properties:
            customerId:
              type: string
              description: Id of the existing customer (on a create conflict).
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Static business API key issued from the dashboard. A business can

        provision **multiple API keys**, each scoped to a configurable set

        of **permissions** (e.g. read transactions, create deposits, etc).

        Permissions are chosen per key at creation time in the dashboard

        and may be revoked by deleting the key. Manage your keys and

        their permissions under **Developer → API keys** in the dashboard.


        A request whose key does not carry the permission the target

        endpoint requires is rejected with `401 Unauthorized`, the same

        response an unrecognised, malformed, revoked or disabled key

        returns. The API does not distinguish the two cases on the wire.


        See the [Permissions
        reference](https://docs.afriex.com/api-reference/permissions)

        for the permission each endpoint requires.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.