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

# Request a payout

> Send crypto from an account wallet over a route and have the provider pay out fiat.

**Requires scope:** `manage:pay-outs` — see [Required scopes](/getting-started/authentication#required-scopes-finance-api).

Requests a **PULL** pay-out against an `ACTIVE` route: Venly sends crypto from the account wallet to the provider's deposit address using the up-front permit, and the provider pays fiat to the beneficiary.

The route already fixes the beneficiary, lane, asset and fiat currency, so you supply only the route, the crypto amount, and an idempotency key.

<Warning>
  **A `201` is not a success signal.** Permit allowance and account-wallet balance are deliberately *not* pre-checked. If either is insufficient, this call still returns `201` and the asynchronous send fails terminally — the pay-out moves to `FAILED` with a `failureReason`.

  Track the real outcome by polling [Get a payout](/api-reference/Finance-API/payouts/get-a-payout) or by [registering a webhook](/api-reference/Finance-API/webhooks/register-a-webhook).
</Warning>

Status advances `REQUESTED → SENDING → PROVIDER_PROCESSING → COMPLETED`. Note that a fiat leg can still bounce **after** completion, moving to `RETURNED`.

PUSH pay-outs are not created here — see [Pay-outs](/guides/finance/payouts#push-pay-outs).

**Idempotency:** the key must be unique per company across all idempotent endpoints. Reuse with a different body, or after the original failed, returns `422`; reuse while the original is in flight returns `409`.

## Errors

The codes this endpoint can return, in addition to the [standard errors](/guides/finance/errors#standard-errors). Branch on `code`, never the message.

| HTTP  | `code`                 | When                                                                 |
| ----- | ---------------------- | -------------------------------------------------------------------- |
| `422` | `route-unresolved`     | the route does not resolve on this account                           |
| `422` | `route-not-active`     | the route is not `ACTIVE`                                            |
| `422` | `unsupported-asset`    | the route's lane is no longer supported for your tenant              |
| `422` | `idempotency-conflict` | the key was reused with a different body, or after a failed original |


## OpenAPI

````yaml api-reference/Finance-API-Specs.yaml POST /accounts/{accountId}/payouts
openapi: 3.1.0
info:
  title: Venly Finance API
  description: >
    REST API for the Venly Finance platform:

    - Party management (Individuals & Organisations)

    - Identity verification — hosted KYC/KYB links and Sumsub share-token
    forwarding

    - Account management with party association

    - Wallet balances & token allowances on supported chains

    - Virtual bank accounts for global pay-in (EUR SEPA, USD ACH/WIRE/RTP/SWIFT)

    - Fiat-to-crypto payment sessions (pay-in)

    - Bank pay-outs — beneficiary allow-listing, routes, and PULL/PUSH pay-outs

    - Account-to-account fiat & crypto transfers

    - EIP-2612 permits and allowances

    - Webhook registration for asynchronous event delivery


    ## Authentication


    All endpoints use OAuth2 client credentials. Obtain a token first, then
    include it in every request:


    **Step 1 — Get a token:**

    ```bash

    curl -X POST
    https://login.venly.io/auth/realms/VenlyFinance/protocol/openid-connect/token
    \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}"
    ```


    **Step 2 — Use the token:**

    ```

    Authorization: Bearer {access_token}

    ```


    Tokens expire after **5 minutes**. Implement refresh logic in your client.
  version: 1.5.0
  contact:
    name: Venly Support
    email: support@venly.io
    url: https://docs.venlyfinance.com
  license:
    name: Proprietary
  x-logo:
    url: https://venlyfinance.com/logo.png
  x-security-contact: security@venly.io
servers:
  - url: https://api.venlyfinance.com/v1
    description: Production
  - url: https://api-staging.venlyfinance.com/v1
    description: Staging
security:
  - OAuth2: []
tags:
  - name: Parties
    description: Party management (Individuals & Organisations)
  - name: Accounts
    description: Account management, party-role associations
  - name: Wallets
    description: Blockchain wallet balances
  - name: Virtual Bank Accounts
    description: Virtual bank account payment references for global payments
  - name: Fiat-to-crypto Payment Sessions
    description: Fiat-to-crypto payment session creation
  - name: Payout Bank Accounts
    description: Allow-listing beneficiary bank accounts as pay-out destinations
  - name: Payout Routes
    description: >-
      Account-scoped routes pairing a beneficiary bank account with a crypto
      deposit asset
  - name: Payouts
    description: Crypto-to-fiat pay-outs over a registered route
  - name: Transfers
    description: Fiat and crypto transfer operations between accounts
  - name: Permits
    description: EIP-712 permit signature management for token approvals
  - name: Supported Assets
    description: >-
      Discover which chains and assets your tenant can settle in, and whether an
      account can use them yet.
  - name: Allowances
    description: Token allowance management for wallets
  - name: Webhooks
    description: Webhook registration and delivery testing
paths:
  /accounts/{accountId}/payouts:
    post:
      tags:
        - Payouts
      summary: Request a payout
      description: >
        **Requires scope:** `manage:pay-outs`


        Requests a **PULL** pay-out against an `ACTIVE` payout route on this
        account: Venly sends crypto

        from the account wallet to the provider's deposit address using the
        up-front permit, and the

        provider pays out fiat to the beneficiary.


        The route fixes the beneficiary, lane, asset and fiat currency, so the
        request carries no fiat

        target, no provider and no funding mode — only the route, the crypto
        amount, and an idempotency

        key.


        This is a synchronous command with an asynchronous tail: the call
        validates, creates the pay-out

        in `REQUESTED`, and returns. The on-chain send and provider hand-off run
        afterwards, advancing

        `REQUESTED → SENDING → PROVIDER_PROCESSING → COMPLETED`.


        **Permit allowance and account-wallet balance are not pre-checked.** If
        either is insufficient,

        this call still returns `201` and the asynchronous send fails terminally
        — the pay-out moves to

        `FAILED` with a `failureReason`. Poll

        [Get a payout](/api-reference/Finance-API/payouts/get-a-payout) or
        subscribe to a webhook rather

        than treating `201` as success.


        **PUSH pay-outs are not created here.** They are created by the provider
        webhook flow after a

        self-custody customer sends to the route's `depositAddress` themselves,
        and appear in the same

        read endpoints with `fundingMode: PUSH`.


        **Idempotency:** `idempotencyKey` is required in the body and must be
        unique per company across

        all idempotent endpoints. A replay with the same key and an identical
        body returns the original

        result. Reuse with a different body, or after the original failed, is
        rejected with

        `422 idempotency-conflict`; reuse while the original is still in
        progress returns

        `409 idempotency-conflict`.
      operationId: requestPayout
      parameters:
        - $ref: '#/components/parameters/AccountId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePayoutRequest'
            example:
              payoutRouteId: b2c3d4e5-f607-4819-a2b3-c4d5e6f70819
              cryptoAmount: 100
              idempotencyKey: payout-2026-07-14-0001
      responses:
        '201':
          description: Payout created (REQUESTED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SinglePayoutResponse'
              example:
                success: true
                result:
                  id: c3d4e5f6-0718-492a-b3c4-d5e6f708192a
                  accountId: b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f
                  payoutRoute:
                    id: b2c3d4e5-f607-4819-a2b3-c4d5e6f70819
                    depositAsset:
                      chain: BASE
                      name: USDC
                    fiatCurrency: USD
                    depositAddress: '0x9A7f4B2c1D3e5F6a8B0c2D4e6F8a0B2c4D6e8F0a'
                    beneficiary:
                      id: a1b2c3d4-e5f6-4708-9a1b-2c3d4e5f6071
                      partyId: 7e3b9c2a-1f4d-4a8b-9c11-2d6e8f0a1b22
                      rail: US_ACH
                      label: Jane's checking account
                      accountHolderName: Jane Doe
                      bankName: Example National Bank
                      details:
                        accountNumberLast4: '9012'
                        abaRoutingNumber: '021000021'
                        accountType: CHECKING
                  rail: US_ACH
                  cryptoAmount: 100
                  settledFiatAmount: null
                  fundingMode: PULL
                  status: REQUESTED
                  sendTxHash: null
                  requestedAt: '2026-07-14T11:00:00Z'
                  completedAt: null
                  failureReason: null
        '400':
          description: >
            Validation failed — a required field is missing, `cryptoAmount` is
            not positive, or

            `account-not-active` (the account is not `ACTIVE`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Forbidden — the caller lacks the `manage:pay-outs` role.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: The account was not found. Also returned for cross-company accounts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            `idempotency-conflict` — the key was reused while the original
            request is still in progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >
            Unprocessable. Possible error codes:

            - `route-unresolved` — the route does not resolve on this account

            - `route-not-active` — the route is not `ACTIVE`

            - `recipient-not-authorized` / `recipient-role-inactive` — the
            recipient gate failed

            - `unsupported-asset` — the route's lane is no longer supported for
            your tenant

            - `idempotency-conflict` — the key was reused with a different body,
            or after a failed original
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    AccountId:
      name: accountId
      in: path
      required: true
      description: Unique account identifier
      schema:
        type: string
        format: uuid
      example: b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f
  schemas:
    CreatePayoutRequest:
      type: object
      description: >
        Requests a PULL pay-out. The route fixes the beneficiary, lane, asset
        and fiat currency, so

        there is no fiat target, no provider field and no funding-mode field.
      required:
        - payoutRouteId
        - cryptoAmount
        - idempotencyKey
      properties:
        payoutRouteId:
          type: string
          format: uuid
          description: >-
            The selected payout route. Must belong to this account and be
            `ACTIVE`.
        cryptoAmount:
          type: number
          description: >
            Crypto amount to send to the provider's deposit address. Must be
            greater than zero, with at

            most 20 integer digits and 18 fractional digits.
          example: 100
        idempotencyKey:
          type: string
          maxLength: 255
          description: >
            Client-supplied idempotency key. Must be unique per company across
            all idempotent

            endpoints.
          example: payout-2026-07-14-0001
    SinglePayoutResponse:
      allOf:
        - $ref: '#/components/schemas/BaseResponse'
        - type: object
          properties:
            result:
              $ref: '#/components/schemas/Payout'
    ErrorResponse:
      type: object
      description: Error response wrapper
      properties:
        success:
          type: boolean
          description: Always false for error responses
          example: false
        errors:
          type: array
          description: List of errors that occurred
          items:
            $ref: '#/components/schemas/ErrorBody'
        result:
          type: object
          nullable: true
          description: Null or omitted when success is false
    BaseResponse:
      type: object
      properties:
        success:
          type: boolean
          description: Indicates whether the request was successful
    Payout:
      type: object
      description: >
        Full pay-out shape, returned on creation and by the detail read. Never
        carries the idempotency

        key, permit material, internal wallet ids, managed transaction ids or
        raw provider data.
      properties:
        id:
          type: string
          format: uuid
        accountId:
          type: string
          format: uuid
        payoutRoute:
          $ref: '#/components/schemas/PayoutRouteInfo'
        rail:
          allOf:
            - $ref: '#/components/schemas/PayoutRail'
          nullable: true
          description: >-
            The fiat rail this pay-out settles over. Null until the route is
            resolved.
        cryptoAmount:
          type: number
          description: Crypto amount sent (PULL) or observed (PUSH)
          example: 100
        settledFiatAmount:
          type: number
          nullable: true
          description: >-
            Fiat amount actually paid out, as reported by the provider. Null
            until `COMPLETED`.
          example: 99.5
        fundingMode:
          $ref: '#/components/schemas/FundingMode'
        status:
          $ref: '#/components/schemas/PayoutStatus'
        sendTxHash:
          type: string
          nullable: true
          description: >
            On-chain send transaction hash — the managed broadcast for PULL, the
            observed customer

            transaction for PUSH. Null until known.
        requestedAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
          nullable: true
        failureReason:
          type: string
          nullable: true
          description: Populated for `REJECTED`, `FAILED` and `RETURNED`.
    ErrorBody:
      type: object
      description: Individual error details
      properties:
        code:
          type: string
          description: Machine-readable error code
          example: invalid-request
        message:
          type: string
          description: Human-readable error message
          example: The request contains invalid parameters.
    PayoutRouteInfo:
      type: object
      description: >-
        The route lane nested in the full pay-out shape, including the masked
        beneficiary.
      properties:
        id:
          type: string
          format: uuid
        depositAsset:
          $ref: '#/components/schemas/DepositAsset'
        fiatCurrency:
          type: string
          example: USD
        depositAddress:
          type: string
          nullable: true
          description: Included only for an `ACTIVE` route on a `SELF_CUSTODY` account.
        beneficiary:
          $ref: '#/components/schemas/PayoutBeneficiary'
    PayoutRail:
      type: string
      description: |
        The fiat rail a payout bank account settles over.
        - `US_ACH` — US ACH transfer. Requires `fiatCurrency: USD`.
      enum:
        - US_ACH
    FundingMode:
      type: string
      description: >
        How a pay-out is funded on-chain.

        - `PULL` — Venly sends from the account wallet using the up-front
        permit. Created via the API

        - `PUSH` — the customer already sent to the route's deposit address
        themselves. Created from the
          observed deposit; self-custody only
      enum:
        - PULL
        - PUSH
    PayoutStatus:
      type: string
      description: >
        Pay-out lifecycle. The PULL happy path is

        `REQUESTED → SENDING → PROVIDER_PROCESSING → COMPLETED`; PUSH pay-outs
        start at

        `PROVIDER_PROCESSING`. Note that a fiat leg can still bounce after
        completion

        (`COMPLETED → RETURNED`).

        - `REQUESTED` — created via the API; awaiting dispatch of the managed
        send leg. PULL only

        - `SENDING` — the managed on-chain send is in flight. PULL only

        - `PROVIDER_PROCESSING` — funds reached the provider deposit address;
        awaiting the fiat pay-out

        - `COMPLETED` — the provider reported the fiat pay-out as completed;
        `settledFiatAmount` is set

        - `REJECTED` — request validation failed before dispatch. PULL only.
        Terminal

        - `FAILED` — the managed send leg failed terminally. PULL only. Terminal

        - `RETURNED` — the provider reported the fiat leg as bounced or
        returned. Terminal
      enum:
        - REQUESTED
        - SENDING
        - PROVIDER_PROCESSING
        - COMPLETED
        - REJECTED
        - FAILED
        - RETURNED
    DepositAsset:
      type: object
      description: >-
        The crypto asset a customer sends to fund a route — a (chain, name)
        pair.
      required:
        - chain
        - name
      properties:
        chain:
          $ref: '#/components/schemas/BlockchainNetwork'
        name:
          type: string
          maxLength: 50
          description: Crypto asset name
          example: USDC
    PayoutBeneficiary:
      type: object
      description: >
        Masked beneficiary bank account nested under a pay-out's route. The raw
        account number is

        never returned.
      properties:
        id:
          type: string
          format: uuid
        partyId:
          type: string
          format: uuid
          description: The owning payee party
        rail:
          $ref: '#/components/schemas/PayoutRail'
        label:
          type: string
        accountHolderName:
          type: string
        bankName:
          type: string
        details:
          $ref: '#/components/schemas/MaskedRailDetails'
    BlockchainNetwork:
      type: string
      description: >
        Supported blockchain network. Availability per chain depends on your
        company's configured

        `supportedChains` — see [Supported chains and
        assets](/guides/finance/supported-chains-and-assets).
      enum:
        - AVALANCHE
        - BASE
        - ETHEREUM
        - POLYGON
        - SOLANA
    MaskedRailDetails:
      type: object
      description: Masked rail credentials, returned on every read path.
      properties:
        accountNumberLast4:
          type: string
          description: >-
            Last 4 digits of the account number. The full number is never
            returned.
          example: '9012'
        abaRoutingNumber:
          type: string
          description: ABA routing number, returned in full (a public bank identifier).
          example: '021000021'
        accountType:
          $ref: '#/components/schemas/AchAccountType'
    AchAccountType:
      type: string
      description: >-
        Type of US-ACH bank account, supplied when registering a payout bank
        account.
      enum:
        - CHECKING
        - SAVINGS
  responses:
    Unauthorized:
      description: Authentication required or failed (401)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            errors:
              - code: unauthenticated
                message: Please authenticate to perform this action.
    InternalServerError:
      description: Unexpected server error (500)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            errors:
              - code: internal-error
                message: An unexpected error occurred. Please try again later.
  securitySchemes:
    OAuth2:
      type: oauth2
      description: >
        OAuth2 client credentials flow. Token endpoints:

        - Staging:
        https://login-staging.venly.io/auth/realms/VenlyFinance/protocol/openid-connect/token

        - Production:
        https://login.venly.io/auth/realms/VenlyFinance/protocol/openid-connect/token
      flows:
        clientCredentials:
          tokenUrl: >-
            https://login-staging.venly.io/auth/realms/VenlyFinance/protocol/openid-connect/token
          scopes: {}

````