---
openapi: 3.1.0
info:
  title: Lumepay API
  description: |
    Lumepay API documentation.

    The Lumepay API gives third-party partners secure server-to-server access to accounts, wallets, currencies, beneficiaries, deposits, withdrawals, and conversions
    initiate currency conversions and review payment activity. Use this API
    to build operational dashboards, initiate supported money movement workflows, reconcile
    transactions, and surface account or wallet information in your own internal systems.

    This documentation describes the external `v1` API contract and the conventions your integration
    should follow from its first request through production use.

    ## Authentication and access

    API requests must include both `X-API-KEY` and `X-API-SECRET` headers. API keys are created
    through the authenticated user's API key management flow. The same user can revoke or rotate those
    credentials when needed. Treat the API secret as a server-side credential only; never expose it in
    browser, mobile, or other client-side code.

    Authentication and access failures are handled consistently across the external API:

    - `401` means the request could not be authenticated because API key headers are missing, invalid,
      revoked, or expired.
    - `403` means the request was authenticated, but the API key owner is not allowed to perform the
      action because of permissions, account state, or access rules.

    Individual endpoint documentation does not repeat these responses unless an endpoint has
    authentication or access behavior that differs from the standard API key flow.

    ## Rate limiting

    External API traffic is rate limited at Cloudflare before it reaches Neptune. Requests to
    `api.staging.lumetrade.com`, `api.lumetrade.com`, `api.staging.lumepay.com`, and `api.lumepay.com`
    under `/api/v1/**` are limited to 33 requests per 10 seconds per client IP and Cloudflare data
    center. Requests above that limit receive HTTP `429` with
    `{"error":"rate_limit_exceeded","message":"API rate limit exceeded"}`. Neptune does not currently
    emit `X-RateLimit-*` quota headers, so clients should still use conservative retry and backoff
    behavior.

    ## Currencies and payment routes

    Supported currencies and payment routes are configuration-driven. When building forms or validation
    rules, use the relevant external, public, or user currency endpoints instead of hard-coding supported
    currency lists from this document. External account and currency endpoints are scoped to the API key
    owner's effective jurisdiction.


    ## Security

    Production and staging integrations must use HTTPS. Rotate API keys immediately if you suspect a
    secret has been exposed. Store secrets in a vault or server-side secret manager, and do not log full
    API secrets.

    ## Error model

    Error responses use Lumepay's standard error envelope:

    ```json
    {
      "code": 1133,
      "message": "Validation error: amount is required",
      "httpStatus": 400,
      "requestId": "req_01HY...",
      "timestamp": "2026-01-01T00:00:00Z",
      "errors": [
        {
          "field": "amount",
          "message": "must be greater than 0"
        }
      ]
    }
    ```

    Common non-authentication error classes include `400` validation failures, `404` resources not found
    for the authenticated owner, `409` insufficient funds or conflicting money movement state, and `500`
    unexpected server errors.

    ## Pagination and filtering

    Current list endpoints may expose reserved query parameters for page, size, sort, date range, and
    status filters. Unless an endpoint explicitly says otherwise, current responses are unpaginated arrays
    and your integration should not depend on server-side pagination behavior yet.

    ## Transaction lifecycle

    Deposits, withdrawals, and conversions move through domain statuses such as pending, processing,
    completed, failed, cancelled, or reversed depending on the resource type. A status is final only when
    the specific endpoint and business flow represent it as terminal. Your integration may poll the read
    endpoints and may subscribe to the documented webhook events for supported deposit, withdrawal, conversion, and test lifecycle notifications.
    Webhooks are delivered at least once; consumers must deduplicate
    using the webhook and delivery identifiers sent with each callback.

    ## Webhooks

    The active v1 webhook catalog is available from `GET /api/v1/webhooks/events`. Use this endpoint as the source of truth for event types supported by the current environment. Do not assume
    unlisted events are emitted.

    Create subscriptions in `PENDING_VERIFICATION`, call the verify endpoint until the callback target has
    returned a successful `webhook.verification` response, then activate the subscription. The same
    subscription can be deactivated and its event list can be updated. Customer-facing secret rotation and
    customer re-enable of system-disabled subscriptions are not supported in v1. Webhook signing secrets are
    platform-managed per-subscription configuration distributed to customers out-of-band by Lume.

    Delivery callbacks include `X-Lume-Webhook-Id`, `X-Lume-Webhook-Delivery-Id`,
    `X-Lume-Webhook-Event`, `X-Lume-Webhook-Timestamp`, and `X-Lume-Webhook-Signature`. The signature is
    `v1=` plus lowercase hex HMAC-SHA256 over `<X-Lume-Webhook-Timestamp>.<raw JSON request body>` using the
    active Lume-provisioned per-subscription webhook signing secret. Verification challenges use the same
    signed callback header contract. Any `2xx` callback response is success. Non-`2xx`, timeout, transport, and
    security validation failures are failed attempts. Neptune retries failed deliveries using the configured
    retry schedule and disables the subscription after the configured maximum failed attempts for a single
    outbox row.

    Financial lifecycle payloads are minimal: they contain the webhook id, event type, and resource type/id.
    Payloads do not expose Sumsub, KYC, KYT, provider ids, wallet metadata, bank details, internal
    reconciliation fields, remediation reasons, or internal case state. Neptune does not promise static
    outbound source IPs for v1 webhook delivery.

    This is version v1 of this API documentation. Last update on June 15, 2026.
  contact:
    name: Lume Forge
  version: v1
servers:
- url: https://api.staging.lumepay.com
tags:
- name: Beneficiaries
  description: Beneficiary endpoints for the authenticated API key owner.
- name: Withdrawals
  description: Withdrawal history endpoints for the authenticated API key owner.
- name: Accounts
  description: Account endpoints for the authenticated API key owner.
- name: Deposits
  description: Deposit history endpoints for the authenticated API key owner.
- name: Payment reasons
  description: Public payment reason catalogue for API consumers.
- name: Conversions
  description: Currency conversion quote and confirmation endpoints.
- name: Webhooks
  description: Webhook event discovery, subscription lifecycle, verification, and
    test delivery endpoints.
- name: Wallets
  description: Wallet endpoints for the authenticated API key owner.
- name: Currencies
  description: Currency catalog endpoints for the authenticated API key owner.
paths:
  "/api/v1/withdrawals/beneficiaries/{beneficiaryId}":
    post:
      tags:
      - Withdrawals
      summary: Create beneficiary withdrawal
      description: Creates a withdrawal using only the saved details from an existing
        confirmed beneficiary. Call GET /api/v1/payment-reasons, then submit its id
        and required text fields together in the flat paymentReasons object.
      operationId: createWithdrawalWithPaymentReasonDocuments
      parameters:
      - name: beneficiaryId
        in: path
        description: Beneficiary id returned by the beneficiary endpoints.
        required: true
        schema:
          type: string
        example: ben_7K9Q2M
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                request:
                  "$ref": "#/components/schemas/CreateWithdrawalFromBeneficiaryRequest"
              required:
              - request
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateWithdrawalFromBeneficiaryRequest"
        required: true
      responses:
        '201':
          description: Withdrawal created successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CreateWithdrawalResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/test-event":
    post:
      tags:
      - Webhooks
      summary: Trigger webhook test event
      description: Queues a `test.event` delivery for active subscriptions owned by
        the authenticated API key owner. This event is a no-op health check and does
        not represent money movement.
      operationId: triggerTestEvent
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '202':
          description: Test event accepted and matching outbox rows queued
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookTestEventResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions":
    get:
      tags:
      - Webhooks
      summary: List webhook subscriptions
      description: Returns webhook subscriptions owned by the authenticated API key
        owner, including their subscribed events.
      operationId: listSubscriptions
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook subscriptions returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
    post:
      tags:
      - Webhooks
      summary: Create webhook subscription
      description: Creates a PENDING_VERIFICATION webhook subscription for a public
        HTTPS callback URL. Each API user may have only one non-deleted subscription
        for a given targetUrl.
      operationId: createSubscription
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/WebhookSubscriptionCreateRequest"
        required: true
      responses:
        '201':
          description: Webhook subscription created successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions/{subscriptionId}/verify":
    post:
      tags:
      - Webhooks
      summary: Verify webhook subscription
      description: Sends a `webhook.verification` challenge to the subscription targetUrl.
        The callback must return any 2xx response. Verification challenges use the
        same signed callback header contract as deliveries. Unverified subscriptions
        can retry this endpoint after the verification cooldown. Verified subscriptions
        return their current details.
      operationId: verifySubscription
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook subscription verified or already verified
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions/{subscriptionId}/events":
    post:
      tags:
      - Webhooks
      summary: Add webhook subscription events
      description: Adds supported event types to a mutable webhook subscription. Existing
        event links are left unchanged.
      operationId: addSubscriptionEvents
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/WebhookSubscriptionEventsUpdateRequest"
        required: true
      responses:
        '200':
          description: Webhook subscription events updated successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
    delete:
      tags:
      - Webhooks
      summary: Remove webhook subscription events
      description: Removes event types from a mutable webhook subscription. The subscription
        must retain at least one event.
      operationId: removeSubscriptionEvents
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/WebhookSubscriptionEventsUpdateRequest"
        required: true
      responses:
        '200':
          description: Webhook subscription events updated successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions/{subscriptionId}/deactivate":
    post:
      tags:
      - Webhooks
      summary: Deactivate webhook subscription
      description: Marks a non-disabled webhook subscription INACTIVE. Inactive subscriptions
        do not receive new deliveries.
      operationId: deactivateSubscription
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook subscription deactivated successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions/{subscriptionId}/activate":
    post:
      tags:
      - Webhooks
      summary: Activate webhook subscription
      description: Activates a verified webhook subscription. Activation is rejected
        until `verifiedAt` is set.
      operationId: activateSubscription
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook subscription activated successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/conversions/quote":
    post:
      tags:
      - Conversions
      summary: Get conversion quote
      description: Requests a currency conversion quote for the authenticated API
        key owner.
      operationId: getQuote
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/ConversionQuoteRequest"
            examples:
              Quote by send amount:
                summary: Request a quote using fromAmount, for example spend 1850
                  ZAR
                description: Quote by send amount
                value:
                  fromAmount: 1850
                  fromCurrency: ZAR
                  toCurrency: USD
                  quoteIntent: client_linked
              Quote by receive amount:
                summary: Request a quote using toAmount, for example receive 100 USD
                description: Quote by receive amount
                value:
                  toAmount: 100
                  fromCurrency: ZAR
                  toCurrency: USD
                  quoteIntent: client_linked
        required: true
      responses:
        '200':
          description: Conversion quote returned successfully
          content:
            application/json:
              schema:
                type: object
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/conversions/quote/confirm":
    post:
      tags:
      - Conversions
      summary: Confirm conversion quote
      description: Confirms a previously issued currency conversion quote for the
        authenticated API key owner.
      operationId: confirmQuote
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/ConfirmConversionRequest"
            examples:
              Confirm by send amount:
                summary: Confirm using fromAmount
                description: Confirm by send amount
                value:
                  fromAmount: 1850
                  fromCurrency: ZAR
                  toCurrency: USD
                  quoteStr: rfq_quote_7K9Q2M
              Confirm by receive amount:
                summary: Confirm using toAmount
                description: Confirm by receive amount
                value:
                  toAmount: 100
                  fromCurrency: ZAR
                  toCurrency: USD
                  quoteStr: rfq_quote_7K9Q2M
        required: true
      responses:
        '200':
          description: Conversion quote confirmed successfully
          content:
            application/json:
              examples:
                Confirmed conversion by send amount:
                  summary: Conversion response returned after confirming a fromAmount
                    quote
                  description: Confirmed conversion by send amount
                  value:
                    fromCurrency: ZAR
                    toCurrency: USD
                    fromAmount: 1850
                    toAmount: 100
                    rate: 0.0541
                    isRateInverted: false
                    status: COMPLETED
                    transactionId: TID4J7K2N
                    createdAt: '2026-05-11T10:15:30Z'
                    completedAt: '2026-05-11T10:15:35'
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/beneficiaries/withdrawals":
    post:
      tags:
      - Beneficiaries
      summary: Create beneficiary withdrawal
      description: Creates a withdrawal using the saved details from the beneficiary
        identified in the body. Call GET /api/v1/payment-reasons, then submit its
        id and required text fields together in the flat paymentReasons object.
      operationId: createWithdrawalWithPaymentReasonDocuments_1
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                request:
                  "$ref": "#/components/schemas/CreateBeneficiaryWithdrawalRequest"
              required:
              - request
          application/json:
            schema:
              "$ref": "#/components/schemas/CreateBeneficiaryWithdrawalRequest"
        required: true
      responses:
        '201':
          description: Withdrawal created successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CreateWithdrawalResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/accounts":
    get:
      tags:
      - Accounts
      summary: List accounts
      description: Returns accounts belonging to the authenticated API key owner for
        currencies enabled in the owner's effective jurisdiction.
      operationId: getAccounts
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Accounts returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/ExternalAccountResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
    post:
      tags:
      - Accounts
      summary: Create account
      description: Creates an account for the authenticated API key owner only when
        the requested currency is enabled in the owner's effective jurisdiction.
      operationId: createAccount
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/ExternalCreateAccountRequest"
        required: true
      responses:
        '201':
          description: Account created successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ExternalAccountResponse"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '409':
          "$ref": "#/components/responses/ConflictError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/withdrawals":
    get:
      tags:
      - Withdrawals
      summary: List withdrawals
      description: Returns the authenticated user's withdrawal history. Pagination,
        sorting, date filtering, and status filtering parameters are reserved for
        this list endpoint; current responses remain unpaginated until server-side
        pagination is enabled.
      operationId: getWithdrawHistory
      parameters:
      - name: page
        in: query
        description: Page number, zero-based.
        required: false
        schema:
          type: integer
          format: int32
        example: 0
      - name: size
        in: query
        description: 'Page size. Planned maximum: 100.'
        required: false
        schema:
          type: integer
          format: int32
        example: 50
      - name: sort
        in: query
        description: 'Sort expression: field,direction.'
        required: false
        schema:
          type: string
        example: completedAt,desc
      - name: dateFrom
        in: query
        description: Reserved inclusive lower timestamp bound.
        required: false
        schema:
          type: string
          format: date-time
        example: '2026-01-01T00:00:00Z'
      - name: dateTo
        in: query
        description: Reserved inclusive upper timestamp bound.
        required: false
        schema:
          type: string
          format: date-time
        example: '2026-01-31T23:59:59Z'
      - name: status
        in: query
        description: Reserved withdrawal status filter.
        required: false
        schema:
          type: string
        example: COMPLETED
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Withdrawals returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/WithdrawResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/withdrawals/{transactionId}":
    get:
      tags:
      - Withdrawals
      summary: Get withdrawal
      description: Returns a single withdrawal belonging to the authenticated API
        key owner.
      operationId: getWithdraw
      parameters:
      - name: transactionId
        in: path
        description: Withdrawal transaction id returned by the withdrawal list endpoint.
        required: true
        schema:
          type: string
        example: wd_5M8Q1P
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Withdrawal returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WithdrawResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/subscriptions/{subscriptionId}":
    get:
      tags:
      - Webhooks
      summary: Get webhook subscription
      description: Returns one webhook subscription owned by the authenticated API
        key owner.
      operationId: getSubscription
      parameters:
      - name: subscriptionId
        in: path
        description: Webhook subscription id returned by create/list endpoints.
        required: true
        schema:
          type: string
        example: whsub_5e737a5abaee46568bd8d530b9b3521a
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook subscription returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WebhookSubscriptionResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/webhooks/events":
    get:
      tags:
      - Webhooks
      summary: List webhook events
      description: Returns the active v1 webhook event catalog. Subscribe with the
        customer-facing `eventType` value, for example `deposit.completed`.
      operationId: listEvents
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook events returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/WebhookEventResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/wallets":
    get:
      tags:
      - Wallets
      summary: List wallets
      description: Returns wallets belonging to the authenticated API key owner.
      operationId: getWallets
      parameters:
      - name: paymentMethodType
        in: query
        description: Optional payment method type filter for fiat wallets.
        required: false
        schema:
          type: string
        example: EFT
      - name: networkName
        in: query
        description: Optional network name filter for crypto wallets.
        required: false
        schema:
          type: string
        example: Ethereum (ETH)
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Wallets returned successfully
          content:
            application/json:
              examples:
                Fiat virtual EFT wallet:
                  summary: Fiat wallet with user-specific EFT bank account details
                  description: Fiat virtual EFT wallet
                  value:
                  - id: wal_3Q8M1K
                    currency: ZAR
                    depositDetails:
                      bankName: Nedbank
                      accountName: Example Customer
                      accountNumber: '9876543210'
                      accountType: Current
                      branchCode: '198765'
                    networkName:
                    paymentMethodType: EFT
                Crypto wallet:
                  summary: Crypto wallet with blockchain address deposit details
                  description: Crypto wallet
                  value:
                  - id: wal_7K9Q2M
                    currency: BTC
                    depositDetails:
                      address: bc1qexample...
                      address_tag: '123456'
                    networkName: Bitcoin
                    paymentMethodType:
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/wallets/{walletId}":
    get:
      tags:
      - Wallets
      summary: Get wallet
      description: Returns one wallet belonging to the authenticated API key owner.
      operationId: getWallet
      parameters:
      - name: walletId
        in: path
        description: Wallet id returned by the wallet list endpoint.
        required: true
        schema:
          type: string
        example: wal_3Q8M1K
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Wallet returned successfully
          content:
            application/json:
              examples:
                Fiat virtual EFT wallet:
                  summary: Fiat wallet with user-specific EFT bank account details
                  description: Fiat virtual EFT wallet
                  value:
                    id: wal_3Q8M1K
                    currency: ZAR
                    depositDetails:
                      bankName: Nedbank
                      accountName: Example Customer
                      accountNumber: '9876543210'
                      accountType: Current
                      branchCode: '198765'
                    networkName:
                    paymentMethodType: EFT
                Crypto wallet:
                  summary: Crypto wallet with blockchain address deposit details
                  description: Crypto wallet
                  value:
                    id: wal_7K9Q2M
                    currency: BTC
                    depositDetails:
                      address: bc1qexample...
                      address_tag: '123456'
                    networkName: Bitcoin
                    paymentMethodType:
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/payment-reasons":
    get:
      tags:
      - Payment reasons
      summary: List payment reasons
      description: Returns configured payment reasons. Put the selected id in paymentReasons.paymentReasonId
        when creating a beneficiary withdrawal. requiredTextFields describes the additional
        flat paymentReasons fields; requiredUploadFile describes required documents
        and their allowedFileTypes.
      operationId: getPaymentReasons
      responses:
        '200':
          description: Payment reasons returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/PaymentReasonResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
  "/api/v1/deposits":
    get:
      tags:
      - Deposits
      summary: List deposits
      description: Returns the authenticated user's deposit history. The status parameter
        filters deposits by their exact status. Pagination, sorting, and date filtering
        parameters are reserved; current responses remain unpaginated until server-side
        pagination is enabled.
      operationId: depositHistory
      parameters:
      - name: page
        in: query
        description: Page number, zero-based.
        required: false
        schema:
          type: integer
          format: int32
        example: 0
      - name: size
        in: query
        description: 'Page size. Planned maximum: 100.'
        required: false
        schema:
          type: integer
          format: int32
        example: 50
      - name: sort
        in: query
        description: 'Sort expression: field,direction.'
        required: false
        schema:
          type: string
        example: completedAt,desc
      - name: dateFrom
        in: query
        description: Lower timestamp bound.
        required: false
        schema:
          type: string
          format: date-time
        example: '2026-01-01T00:00:00Z'
      - name: dateTo
        in: query
        description: Upper timestamp bound.
        required: false
        schema:
          type: string
          format: date-time
        example: '2026-01-31T23:59:59Z'
      - name: status
        in: query
        description: Deposit status filter.
        required: false
        schema:
          type: string
          enum:
          - SUBMITTED
          - PENDING
          - PROCESSING
          - COMPLIANCE_PENDING
          - COMPLETED
          - FAILED
          - CANCELLED
          - REVERSED
        example: COMPLETED
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deposits returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/DepositResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/deposits/{transactionId}":
    get:
      tags:
      - Deposits
      summary: Get deposit
      description: Returns a single deposit belonging to the authenticated API key
        owner.
      operationId: getDeposit
      parameters:
      - name: transactionId
        in: path
        description: Deposit Transaction id returned by the deposit list endpoint.
        required: true
        schema:
          type: string
        example: dep_9H2K7P
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deposit returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/DepositResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/currencies":
    get:
      tags:
      - Currencies
      summary: List currencies
      description: Returns enabled currencies for the authenticated API key owner's
        effective jurisdiction.
      operationId: getCurrencies
      parameters:
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Currencies returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/ExternalCurrencyResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/beneficiaries":
    get:
      tags:
      - Beneficiaries
      summary: List beneficiaries
      description: Returns confirmed beneficiaries belonging to the authenticated
        API key owner.
      operationId: getBeneficiaries
      parameters:
      - name: currency
        in: query
        description: Optional beneficiary currency code filter.
        required: false
        schema:
          type: string
        example: ZAR
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Beneficiaries returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/BeneficiaryResponse"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/beneficiaries/{beneficiaryId}":
    get:
      tags:
      - Beneficiaries
      summary: Get beneficiary
      description: Returns a confirmed beneficiary belonging to the authenticated
        API key owner.
      operationId: getBeneficiary
      parameters:
      - name: beneficiaryId
        in: path
        description: Beneficiary id returned by the beneficiary list endpoint.
        required: true
        schema:
          type: string
        example: ben_7K9Q2M
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Beneficiary returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BeneficiaryResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/accounts/{accountId}":
    get:
      tags:
      - Accounts
      summary: Get account
      description: Returns one account belonging to the authenticated API key owner
        when its currency is enabled in the owner's effective jurisdiction.
      operationId: getAccount
      parameters:
      - name: accountId
        in: path
        description: Account id returned by the account list endpoint.
        required: true
        schema:
          type: string
        example: acc_8F4J2N
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Account returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ExternalAccountResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
  "/api/v1/accounts/currency/{currency}":
    get:
      tags:
      - Accounts
      summary: Get account by currency
      description: Returns one account belonging to the authenticated API key owner
        for the requested currency when that currency is enabled in the owner's effective
        jurisdiction.
      operationId: getAccountByCurrency
      parameters:
      - name: currency
        in: path
        description: Currency for the account lookup.
        required: true
        schema:
          type: string
        example: ZAR
      - name: X-API-KEY
        in: header
        description: Public API key issued from the Lumepay API key management flow.
        required: true
        schema:
          type: string
      - name: X-API-SECRET
        in: header
        description: API secret paired with the public API key.
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Account returned successfully
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ExternalAccountResponse"
        '404':
          "$ref": "#/components/responses/NotFoundError"
        '429':
          "$ref": "#/components/responses/RateLimitError"
        '403':
          "$ref": "#/components/responses/ForbiddenError"
        '500':
          "$ref": "#/components/responses/InternalServerError"
        '400':
          "$ref": "#/components/responses/BadRequestError"
        '401':
          "$ref": "#/components/responses/UnauthorizedError"
components:
  schemas:
    CreateWithdrawalFromBeneficiaryRequest:
      type: object
      description: Request to create a withdrawal from the beneficiary identified
        in the request path.
      properties:
        amount:
          type: number
          description: Withdrawal amount in the beneficiary currency.
          example: 101
        paymentReasons:
          type: object
          additionalProperties:
            type: object
          description: Flat payment reason object. Include paymentReasonId and values
            keyed by requiredTextFields from GET /api/v1/payment-reasons.
          example:
            paymentReasonId: 18
            regulatoryBodyName: Financial Sector Conduct Authority
            licenceRegistrationReference: FSP-123456
      required:
      - amount
    CreateWithdrawalResponse:
      type: object
      description: Withdrawal creation response returned by the API.
      properties:
        transactionId:
          type: string
          description: Created withdrawal transaction id.
          example: WD5M8Q1P
        status:
          type: string
          description: Created withdrawal status.
          enum:
          - PENDING
          - ACCEPTED
          - AWAITING_CONFIRMATION
          - PROCESSING
          - COMPLIANCE_PENDING
          - COMPLETED
          - FAILED
          - CANCELLED
          - REVERSED
          example: PENDING
    WebhookTestEventResponse:
      type: object
      description: Result of queuing a no-op webhook test event.
      properties:
        eventType:
          type: string
          description: Queued event type.
          example: test.event
        resourceInternalTransactionId:
          type: string
          description: Synthetic resource id for this test event.
          example: test_96e3d0fb6f7445098ec197bb4350e993
        matchedSubscriptionCount:
          type: integer
          format: int32
          description: Number of active subscriptions matched by the test event.
          example: 1
        queuedOutboxCount:
          type: integer
          format: int32
          description: Number of outbox rows queued for delivery.
          example: 1
        outboxIds:
          type: array
          items:
            example: evt_8410b4d047d44572b09f2e828c3ccb5a
    WebhookSubscriptionCreateRequest:
      type: object
      description: Request to create a webhook subscription for the authenticated
        API key owner.
      properties:
        name:
          type: string
          description: Optional customer label for the subscription.
          example: Ops webhook alerts
        targetUrl:
          type: string
          description: Public HTTPS callback URL. Private, loopback, link-local, metadata,
            and internal service destinations are rejected.
          example: https://example.com/webhooks
        eventTypes:
          type: array
          items:
            description: Webhook event type.
            example: deposit.completed
          minItems: 1
          uniqueItems: true
      required:
      - targetUrl
    WebhookEventResponse:
      type: object
      description: Customer-facing webhook event catalog entry.
      properties:
        eventType:
          type: string
          description: Customer-facing event key used in subscription requests.
          example: deposit.completed
        resourceType:
          type: string
          description: Resource category for the event.
          example: deposit
        event:
          type: string
          description: Event name within the resource category.
          example: completed
        displayName:
          type: string
          description: Human-readable event name.
          example: Deposit completed
        description:
          type: string
          description: Short description of when the event is emitted.
          example: Deposit has completed.
    WebhookSubscriptionResponse:
      type: object
      description: Webhook subscription owned by the authenticated API key owner.
      properties:
        id:
          type: string
          description: Public webhook subscription id.
          example: whsub_5e737a5abaee46568bd8d530b9b3521a
        name:
          type: string
          description: Optional customer label.
          example: Ops webhook alerts
        targetUrl:
          type: string
          description: Public HTTPS callback URL.
          example: https://example.com/webhooks
        status:
          type: string
          description: Subscription status. PENDING_VERIFICATION requires verify before
            activation. INACTIVE subscriptions receive no new deliveries. DISABLED
            subscriptions are not customer re-enabled in v1.
          enum:
          - PENDING_VERIFICATION
          - ACTIVE
          - INACTIVE
          - DISABLED
          example: PENDING_VERIFICATION
        events:
          type: array
          items:
            "$ref": "#/components/schemas/WebhookEventResponse"
        verifiedAt:
          type: string
          format: date-time
          description: UTC timestamp when the callback endpoint was last verified.
          example: '2026-06-12T15:03:23Z'
        createdAt:
          type: string
          format: date-time
          description: UTC creation timestamp.
          example: '2026-06-12T15:01:00Z'
        updatedAt:
          type: string
          format: date-time
          description: UTC last update timestamp.
          example: '2026-06-12T15:03:23Z'
    WebhookSubscriptionEventsUpdateRequest:
      type: object
      description: Request to add or remove event types on an existing webhook subscription.
      properties:
        eventTypes:
          type: array
          items:
            description: Webhook event type.
            example: deposit.completed
          minItems: 1
          uniqueItems: true
    ConversionQuoteRequest:
      type: object
      properties:
        fromAmount:
          type: number
          description: How much the customer wants to send or pay. Provide either
            fromAmount or toAmount, not both.
          example: 1850
        toAmount:
          type: number
          description: How much the customer wants to receive. Provide either toAmount
            or fromAmount, not both.
          example: 100
        fromCurrency:
          type: string
          description: Currency the customer sends or pays with.
          example: ZAR
        toCurrency:
          type: string
          description: Currency the customer wants to receive.
          example: USD
        quoteIntent:
          type: string
          description: Use client_linked when the customer may confirm this quote.
            Use indicative when the customer only needs a preview price.
          enum:
          - indicative
          - client_linked
          example: client_linked
          pattern: "(?i)indicative|client_linked"
    ConfirmConversionRequest:
      type: object
      properties:
        fromAmount:
          type: number
          description: Amount the customer sends or pays from the quote response.
            Provide either fromAmount or toAmount, not both.
          example: 1850
        toAmount:
          type: number
          description: Amount the customer receives from the quote response. Provide
            either toAmount or fromAmount, not both.
          example: 100
        fromCurrency:
          type: string
          description: Currency the customer sends or pays with.
          example: ZAR
        quoteStr:
          type: string
          description: Quote reference returned by /api/v1/conversions/quote.
          example: rfq_quote_7K9Q2M
        toCurrency:
          type: string
          description: Currency the customer receives.
          example: USD
    CreateBeneficiaryWithdrawalRequest:
      type: object
      description: Request to create a withdrawal from a beneficiary.
      properties:
        beneficiaryId:
          type: string
          description: Beneficiary id returned by the beneficiary endpoints.
          example: ben_7K9Q2M
        amount:
          type: number
          description: Withdrawal amount in the beneficiary currency.
          example: 101
        paymentReasons:
          type: object
          additionalProperties:
            type: object
          description: Flat payment reason object. Include paymentReasonId and values
            keyed by requiredTextFields from GET /api/v1/payment-reasons.
          example:
            paymentReasonId: 18
            regulatoryBodyName: Financial Sector Conduct Authority
            licenceRegistrationReference: FSP-123456
      required:
      - amount
    ExternalCreateAccountRequest:
      type: object
      properties:
        currency:
          type: string
          description: Currency code for the account to create.
          example: ZAR
    ExternalAccountResponse:
      type: object
      properties:
        id:
          type: string
          description: Account id used by external account endpoints.
          example: acc_8F4J2N
        currency:
          type: string
          description: Account currency.
          example: ZAR
        balance:
          type: number
          description: Available account balance.
          example: 2500
        lockedBalance:
          type: number
          description: Locked account balance.
          example: 0
    JsonNode:
      type: object
      additionalProperties: true
      description: Raw withdrawal details stored as JSON object.
      example:
        bankName: FNB
        accountNumber: '1234567890'
      properties:
        pojo:
          type: boolean
        int:
          type: boolean
        double:
          type: boolean
        bigDecimal:
          type: boolean
        bigInteger:
          type: boolean
        nodeType:
          type: string
          enum:
          - ARRAY
          - BINARY
          - BOOLEAN
          - MISSING
          - 'NULL'
          - NUMBER
          - OBJECT
          - POJO
          - STRING
        container:
          type: boolean
        short:
          type: boolean
        binary:
          type: boolean
        long:
          type: boolean
        integralNumber:
          type: boolean
        floatingPointNumber:
          type: boolean
        missingNode:
          type: boolean
        string:
          type: boolean
        boolean:
          type: boolean
        number:
          type: boolean
        object:
          type: boolean
        textual:
          type: boolean
          deprecated: true
        valueNode:
          type: boolean
        array:
          type: boolean
        empty:
          type: boolean
        'null':
          type: boolean
        float:
          type: boolean
        embeddedValue:
          type: boolean
    WithdrawResponse:
      type: object
      description: Withdrawal transaction returned by the API.
      properties:
        amount:
          type: number
          description: Withdrawal amount.
          example: 101
        fee:
          type: number
          description: Withdrawal fee.
          example: 1
        currency:
          type: string
          description: Withdrawal currency code.
          example: ZAR
        withdrawDetails:
          "$ref": "#/components/schemas/JsonNode"
          additionalProperties: true
          description: Raw withdrawal details stored as JSON object.
          example:
            bankName: FNB
            accountNumber: '1234567890'
        status:
          type: string
          description: Withdrawal status.
          enum:
          - PENDING
          - ACCEPTED
          - AWAITING_CONFIRMATION
          - PROCESSING
          - COMPLIANCE_PENDING
          - COMPLETED
          - FAILED
          - CANCELLED
          - REVERSED
          example: COMPLETED
        transactionId:
          type: string
          description: Transaction id for withdrawal lookups.
          example: wd_5M8Q1P
        createdAt:
          type: string
          format: date-time
          description: Withdrawal creation timestamp.
          example: '2026-05-11T10:15:30Z'
        completedAt:
          type: string
          format: date-time
          description: Completion timestamp.
          example: '2026-05-11T10:20:30'
        isFiat:
          type: boolean
          description: Whether this withdrawal used a fiat route.
          example: true
    PaymentReasonResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: Payment reason id to submit as paymentReasonId.
          example: 1
        reason:
          type: string
          description: Consumer-facing payment reason.
          example: Payment for goods
        requiredTextFields:
          type: object
          additionalProperties:
            type: object
          description: Text inputs required in paymentReasonTextValues, keyed by input
            name.
        requiredUploadFile:
          type: object
          additionalProperties:
            type: object
          description: Required upload inputs keyed by document name, including allowedFileTypes
            metadata.
    DepositResponse:
      type: object
      description: Deposit transaction returned by the API.
      properties:
        currency:
          type: string
          description: Deposit currency code.
          example: USDT
        transactionId:
          type: string
          description: Transaction id for deposit lookups.
          example: DP9H2K7P
        amount:
          type: number
          description: Deposit amount.
          example: 100
        fee:
          type: number
          description: Deposit fee.
          example: 0
        status:
          type: string
          description: Deposit status.
          enum:
          - SUBMITTED
          - PENDING
          - PROCESSING
          - COMPLIANCE_PENDING
          - COMPLETED
          - FAILED
          - CANCELLED
          - REVERSED
          example: COMPLETED
        completedAt:
          type: string
          format: date-time
          description: Completion timestamp.
          example: '2026-05-11T10:15:30'
    ExternalCurrencyResponse:
      type: object
      properties:
        currency:
          type: string
          description: Currency code.
          example: ZAR
        name:
          type: string
          description: Currency display name.
          example: South African Rand
    BeneficiaryResponse:
      type: object
      description: Beneficiary available to the authenticated API key user.
      properties:
        id:
          type: string
          description: Beneficiary id used by external beneficiary endpoints.
          example: ben_7K9Q2M
        currency:
          type: string
          description: Saved beneficiary currency.
          example: ZAR
        nickname:
          type: string
          description: Beneficiary nickname.
          example: FNB Savings
        withdrawFormDetails:
          "$ref": "#/components/schemas/JsonNode"
          additionalProperties: true
          description: Saved withdrawal form details for the beneficiary.
          example:
            bankName: FNB
            accountNumber: '1234567890'
            accountHolderName: Jane Example
        createdAt:
          type: string
          format: date-time
          description: Beneficiary creation timestamp.
          example: '2026-05-11T10:15:30Z'
    ApiResponse:
      type: object
      properties:
        code:
          type: integer
          format: int32
          example: 400
        message:
          type: string
          example: Request validation failed
        httpStatus:
          type: integer
          format: int32
          example: 400
        requestId:
          type: string
          example: req_01HY0000000000000000000000
        timestamp:
          type: string
          format: date-time
          example: '2026-01-01T00:00:00Z'
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
                example: amount
              message:
                type: string
                example: must be greater than 0
  responses:
    BadRequestError:
      description: Request validation failed
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 400
            requestId: req_01HY0000000000000000000000
            message: Request validation failed
            code: 400
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
    UnauthorizedError:
      description: API key authentication failed
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 401
            requestId: req_01HY0000000000000000000000
            message: API key authentication failed
            code: 401
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
    RateLimitError:
      description: Cloudflare API rate limit exceeded
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: rate_limit_exceeded
              message:
                type: string
                example: API rate limit exceeded
          example:
            error: rate_limit_exceeded
            message: API rate limit exceeded
    ForbiddenError:
      description: Authenticated API key is not allowed to perform this action
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 403
            requestId: req_01HY0000000000000000000000
            message: Authenticated API key is not allowed to perform this action
            code: 403
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
    NotFoundError:
      description: Resource was not found for the authenticated API key owner
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 404
            requestId: req_01HY0000000000000000000000
            message: Resource was not found for the authenticated API key owner
            code: 404
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
    ConflictError:
      description: Request conflicts with current resource or money movement state
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 409
            requestId: req_01HY0000000000000000000000
            message: Request conflicts with current resource or money movement state
            code: 409
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
    InternalServerError:
      description: Unexpected server error
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ApiResponse"
          example:
            httpStatus: 500
            requestId: req_01HY0000000000000000000000
            message: Unexpected server error
            code: 500
            timestamp: '2026-01-01T00:00:00Z'
            errors: []
