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

# Create a transaction

> Register a mobile money payment request and get the references to keep.

This operation registers a payment request for the authenticated account and returns the references to keep. It only supports `Ipay-Payment-Type: mobile` (mobile money); other payment methods go through [payment by link](https://docs.i-pay.money/en/api-reference/payment-by-link/create-a-payment-link.md). The list of methods is in [Payouts and fees](https://docs.i-pay.money/en/guides/payouts-and-fees.md#fees-by-payment-method).

> [!WARNING]
>
> The status returned at creation is not final. Always check the final status on the server side, with [Retrieve a transaction](https://docs.i-pay.money/en/api-reference/transactions/retrieve-a-payment.md) or through [webhooks](https://docs.i-pay.money/en/guides/webhooks.md), before delivering an order.

* **Validated account.** The account must be validated for the target environment, otherwise the API responds `403` without creating a transaction.
* **Environments.** Sandbox and Live are two separate accounts, with separate keys: a Sandbox key used with `Ipay-Target-Environment: live` is refused, and vice versa. See [Authentication](https://docs.i-pay.money/en/api-reference/authentication.md).
* **`transaction_id`.** This is your own order reference, unique for your account. You will find it under the name `external_reference`, and it is accepted as an identifier by [Retrieve a transaction](https://docs.i-pay.money/en/api-reference/transactions/retrieve-a-payment.md).
* **Minimum amount.** `25` XOF in Live for `mobile`. The minimum is different in Sandbox: see [Sandbox and testing](https://docs.i-pay.money/en/guides/sandbox-and-testing.md).
* **References to keep.** `reference` is the iMoney Encaissement identifier of the transaction, accepted by the retrieval operations. `public_reference` is the reference you can show to your customer.

## Duplicates and idempotency

There is no idempotency key header. Creation applies duplicate detection **at the application level** on `external_reference`, that is, on the `transaction_id` you send. **In Live**, a second call with the same value receives `422` (`{"status": "failed", "message": "External Reference Not Valid"}`) and does not create a second transaction. **In Sandbox, the shape of the response to a duplicate is not guaranteed to match** the Live one: to be confirmed with support before using it as an automated test assertion.

> [!WARNING]
>
> This detection is not guaranteed under concurrent calls: two requests sent at the same moment with the same `transaction_id` may both succeed. Generate a unique `transaction_id` for each attempt, and check the status with [Retrieve a transaction](https://docs.i-pay.money/en/api-reference/transactions/retrieve-a-payment.md) before retrying a transaction for which you did not receive a response.

## Limits

No rate limit is documented for this operation: to be confirmed with support before bulk sending. The error messages common to all operations are gathered in [Errors](https://docs.i-pay.money/en/api-reference/errors.md).


## OpenAPI

````yaml en/openapi.yaml POST /payments
openapi: 3.1.0
info:
  title: iPayMoney Merchant API
  version: 1.0.0
  description: >-
    v1 merchant contracts verified against the Rails routes, controllers and
    services. Partner contracts and non-operational v2 routes are not part of
    this public specification. Payment links must use private random references.
servers:
  - url: https://i-pay.money/api/v1
    description: Sandbox or Live depending on the headers and the key used
security: []
tags:
  - name: Payments
    description: Creation and tracking of merchant transactions
  - name: Payment links
    description: Hosted payment links
paths:
  /payments:
    post:
      tags:
        - Payments
      summary: Create a transaction
      parameters:
        - $ref: '#/components/parameters/TargetEnvironment'
        - $ref: '#/components/parameters/PaymentType'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentRequest'
            examples:
              sandboxMobile:
                summary: Successful Sandbox mobile transaction
                value:
                  amount: 1000
                  country: NE
                  currency: XOF
                  msisdn: '40410000000'
                  transaction_id: ORDER-SANDBOX-001
                  customer_name: Client Test
      responses:
        '200':
          description: Transaction registered. The final status must be checked separately.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentCreateResponse'
        '400':
          description: >-
            Invalid header or authentication parameter, or invalid request body
            (missing field or value outside the constraints).
          x-verified: >-
            Internal verification note (not intended for the public
            documentation). Detected by `ApiAuthService#authenticate`
            (app/services/api_auth_service.rb:20-26) or
            `Live::PaymentService#request_payment`
            (app/services/live/payment_service.rb). Exact messages verified on
            2026-09-16.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingHeaderParams:
                  summary: Missing authentication header
                  value:
                    message: 'Bad Request: Missing params'
                notAllowedEnvironment:
                  summary: Ipay-Target-Environment not allowed
                  value:
                    message: 'Bad Request: Not Allowed Environment'
                notAllowedPaymentType:
                  summary: Ipay-Payment-Type not allowed
                  value:
                    message: 'Bad Request: Not Allowed Payment Type'
                missingAuthenticationKey:
                  summary: Authorization Bearer missing or malformed
                  value:
                    message: 'Bad Request: Missing Authentication Key'
                missingBodyParams:
                  summary: >-
                    Required field missing from the body (amount, country,
                    currency, msisdn, transaction_id or customer_name)
                  value:
                    message: 'Bad Request: Missing Body Params'
                countryNotAllowed:
                  summary: country other than NE or BJ
                  value:
                    message: 'Bad Request: Country Not Allowed'
                currencyNotAllowed:
                  summary: currency other than XOF
                  value:
                    message: 'Bad Request: Currency Not Allowed'
                amountNotValid:
                  summary: >-
                    amount below the applicable minimum (25 XOF in Live, 50 XOF
                    in Sandbox)
                  value:
                    message: 'Bad Request: Amount Not Valid'
                incorrectMsisdn:
                  summary: msisdn in an invalid format for the method
                  value:
                    message: 'Bad Request: Incorrect MSISDN'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '406':
          $ref: '#/components/responses/NotAcceptable'
        '422':
          description: >-
            External reference (`transaction_id`) already used for this account:
            duplicate detection applied at the application level, not guaranteed
            under concurrent calls — the API does not expose any idempotency
            key. This code also covers a payment that is invalid for another
            validation reason.
          x-verified: >-
            Internal verification note (not intended for the public
            documentation). See `Live::PaymentService#error_response`,
            app/services/live/payment_service.rb:994-1005.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                duplicateExternalReference:
                  summary: transaction_id already used for this account
                  value:
                    status: failed
                    message: External Reference Not Valid
      security:
        - bearerAuth: []
components:
  parameters:
    TargetEnvironment:
      name: Ipay-Target-Environment
      in: header
      required: true
      schema:
        type: string
        enum:
          - sandbox
          - live
    PaymentType:
      name: Ipay-Payment-Type
      in: header
      required: true
      description: >-
        Payment method. `POST /payments` only supports `mobile` (mobile money).
        Other payment methods (bank card and other channels) are not used here:
        they are available through payment links (`POST /external_payments`).
        Full list: [payment
        methods](/en/guides/payouts-and-fees#fees-by-payment-method).
      x-verified: >-
        Internal verification note (not intended for the public documentation).
        Scope decision: only the `mobile` value is documented for `POST
        /payments`; the other values handled by `Live::PaymentService` are still
        accepted by the API but belong to the "payment link" flow.
        `external_payment` is reserved for `POST /external_payments` (dedicated
        parameter of that operation).
      schema:
        type: string
        const: mobile
        example: mobile
  schemas:
    PaymentRequest:
      type: object
      description: >-
        Body required to create a mobile money payment (`Ipay-Payment-Type:
        mobile`). The minimum amount is 25 XOF (50 XOF in Sandbox). Below that,
        the API responds 400 `Bad Request: Amount Not Valid`. For other payment
        methods, use payment links.
      x-verified: >-
        Internal verification note (not intended for the public documentation).
        Body required by `Live::PaymentService#request_payment`
        (app/services/live/payment_service.rb). `mobile` threshold ≥ 25 verified
        on 2026-09-16 (`verify_mobile_amount_value`). The thresholds of the
        other types are no longer documented here (scope reduced to `mobile`).
      required:
        - amount
        - country
        - currency
        - msisdn
        - transaction_id
        - customer_name
      properties:
        amount:
          type: integer
          minimum: 25
          example: 1000
          description: >-
            Amount to debit, in XOF (integer). Minimum: 25 XOF in Live, 50 XOF
            in Sandbox.
        country:
          type: string
          enum:
            - NE
            - BJ
          example: NE
        currency:
          type: string
          const: XOF
        msisdn:
          type: string
          example: '40410000000'
        transaction_id:
          type: string
          description: Unique reference within the account
        customer_name:
          type: string
    PaymentCreateResponse:
      type: object
      required:
        - status
        - reference
        - public_reference
      properties:
        status:
          type: string
          example: succeeded
        reference:
          type: string
        public_reference:
          type: string
        meta_data:
          type: object
          additionalProperties: true
    Error:
      type: object
      properties:
        message:
          type: string
        status:
          type: string
          description: >-
            Only present on some application failures, e.g. a duplicate payment
            on `POST /payments`, where `status` is always `failed`. Absent from
            the other errors of this contract (e.g. 404 `Payment Not Found`,
            header/authentication errors).
          x-verified: >-
            Internal verification note (not intended for the public
            documentation). See `Live::PaymentService#error_response`
            (app/services/live/payment_service.rb:994-1005).
          example: failed
  responses:
    Unauthorized:
      description: Invalid key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Account or environment not available
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotAcceptable:
      description: Content-Type other than application/json
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Secret key of the Sandbox or Live account

````

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