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

# Payouts

> Run Noah payout workflows through Portal from channel discovery and form rendering to quote and payout initiation.

## Overview

Noah payouts let your client convert stablecoins to fiat and disburse through local payout rails.

## 1. Prerequisite: approved KYC

Ensure the client has completed Noah onboarding and has `APPROVED` status.

See [Noah KYC onboarding](/integrations/On-Off-Ramp/noah-kyc).

## 2. Get supported payout countries

```bash theme={null}
curl --request GET \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts/countries \
  --header 'Authorization: Bearer [token]'
```

The `countries` map includes many additional country/currency combinations based on Noah support.

## 3. Get payout channels

Call `GET /payouts/channels` with `country`, `cryptoCurrency`, and `fiatCurrency`.

```bash theme={null}
curl --request GET \
  --url 'https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts/channels?country=BR&cryptoCurrency=USDC_TEST&fiatCurrency=BRL' \
  --header 'Authorization: Bearer [token]'
```

<Note>
  `fiatAmount` is optional on channels lookup and can improve quote context. `pageToken` is also optional and can be used to fetch the next page when returned.
</Note>

## 4. (Optional) Get saved payment methods

Call `GET /payouts/payment-methods` to list saved payout destinations for the approved customer.

If you want to use a saved payout destination, you can pass its `id` as `paymentMethodId` in `POST /payouts/quote`.

```bash theme={null}
curl --request GET \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts/payment-methods \
  --header 'Authorization: Bearer [token]'
```

Example response (truncated to key fields):

```json theme={null}
{
  "data": {
    "paymentMethods": [
      {
        "id": "Bank/Ach/USD/021000021/123456789/cmmw6tzqc00003saqzbgwueb5",
        "paymentMethodCategory": "Bank",
        "customerId": "cmmw6tzqc00003saqzbgwueb5",
        "country": "US",
        "capabilities": {
          "payinTo": false,
          "payoutFrom": false,
          "payoutTo": true
        },
        "displayDetails": {
          "type": "FiatPaymentMethodBankDisplay",
          "accountNumber": "123456789",
          "bankCode": "021000021"
        },
        "accountHolderDetails": {
          "name": {
            "firstName": "John",
            "lastName": "Mock-Doe"
          }
        },
        "issuerDetails": {}
      }
    ]
  }
}
```

`pageToken` may also be present when pagination is returned.

## 5. Get dynamic form for selected channel

Use `GET /payouts/channels/:channelId/form` and render the returned `formSchema`.

```bash theme={null}
curl --request GET \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts/channels/[channelId]/form \
  --header 'Authorization: Bearer [token]'
```

Example response (truncated to key fields):

```json theme={null}
{
  "data": {
    "formMetadata": {
      "contentHash": "eda5d74d98e3e7429644fd7f74d4979638eca0e9aa7422091a600d85c1f3422a"
    },
    "formSchema": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "AccountHolderName": {
          "type": "object",
          "title": "Account Holder Name"
        },
        "AccountHolderAddress": {
          "type": "object",
          "title": "Account Holder Address"
        },
        "BankDetails": {
          "type": "object",
          "title": "Bank Details"
        },
        "PaymentPurpose": {
          "type": "string",
          "title": "Payment Purpose"
        }
      },
      "required": [
        "AccountHolderName",
        "AccountHolderAddress",
        "BankDetails",
        "PaymentPurpose"
      ]
    }
  }
}
```

`formSchema` can include additional nested validation rules (`allOf`, `if`/`then`, `minLength`, `maxLength`, `pattern`) that should be rendered and validated in your form UI.

## 6. Quote payout

Call `POST /payouts/quote` to validate form data and get fee + estimate.
`paymentMethodId` is optional and is only needed when quoting against a saved payment method from step 4.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts/quote \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --data '{
    "channelId": "214eab50-e22b-5e0f-b487-37dc1addee90",
    "cryptoCurrency": "USDC_TEST",
    "fiatAmount": "0.01",
    "fiatCurrency": "USD",
    "form": {
      "AccountHolderName": {
        "AccountHolderType": "Individual",
        "Name": {
          "FirstName": "John",
          "LastName": "Mock-Doe"
        }
      },
      "AccountHolderAddress": {
        "Address": "123 Main St",
        "City": "Miami",
        "PostalCode": "33139",
        "State": "FL"
      },
      "BankDetails": {
        "AccountNumber": "900711447286",
        "BankCode": "101019644",
        "AccountType": "Checking"
      },
      "PaymentPurpose": "Personal transfer",
      "Reference": "Optional reference"
    }
  }'
```

Example response (truncated to key fields):

```json theme={null}
{
  "data": {
		"payoutId": "cmmyatsdk0000wy2en3lof5jj",
		"formSessionId": "d5f81409-5306-476b-84d6-6be2b5656a5a",
		"cryptoAmountEstimate": "0.010004",
		"totalFee": "0.01"
	}
}
```

## 7. Initiate payout

Call `POST /payouts` using `payoutId` returned from quote.

`nonce` must be unique per transaction attempt (max 36 characters). Reuse the same nonce only when retrying the same payout request for idempotency.

```bash theme={null}
curl --request POST \
  --url https://api.portalhq.io/api/v3/clients/me/integrations/noah/payouts \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --data '{
    "payoutId":"cmmyatsdk0000wy2en3lof5jj",
    "sourceAddress":"DEeUjZtzWWiM6sJPBFsR5iL9AeSEtvtuh4ycHM4LHyTK",
    "expiry":"2026-03-24T23:59:59Z",
    "nonce":"nonce-20260320-02388933",
    "network":"solana:devnet"
  }'
```

Example response:

```json theme={null}
{
	"data": {
		"destinationAddress": "4dguFohaa8F9MvRtZecGsHCvnGqzYPyGyusP6BWu4zLK",
		"conditions": [
			{
				"amountConditions": [
					{
						"comparisonOperator": "EQ",
						"value": "0.010004"
					}
				],
				"cryptoCurrency": "USDC_TEST",
				"destinationAddress": "4dguFohaa8F9MvRtZecGsHCvnGqzYPyGyusP6BWu4zLK",
				"network": "SolanaDevnet"
			}
		]
	}
}
```

<Note>
  After receiving this response, submit the onchain transfer to the returned `destinationAddress` and ensure it satisfies the returned `conditions` (network and amount constraints).
</Note>

## 8. Configure Noah webhooks for payout updates

Set up Noah webhooks in Noah Dashboard to receive payout lifecycle and transaction updates directly from Noah.

* Subscribe to `Transaction` events for payout execution and completion updates.
* [Noah Webhooks](/integrations/On-Off-Ramp/noah-webhooks)

## 9. Monitor payout progress

Use `Transaction` webhook events and Noah Dashboard delivery logs to track payout execution and reconciliation.

<Warning>
  `POST /payouts` only works for payout intents in `PREPARED` status (created by `POST /payouts/quote`).
</Warning>
