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

# Payout with Node.js

> Send a mobile-money payout using the Dollr API from Node.js.

Send funds to a recipient's registered wallet using **payout sessions** and **executions**. For collection, see [Collect with Node.js](/guides/collect-with-nodejs).

<Note>
  **API Reference:** [Payout session](/api-reference/sessions/create-payout-session) · [Payout](/api-reference/executions/payout) · [Payout status](/api-reference/status/get-payout-status)
</Note>

<Warning>
  Payouts require a **fully verified** merchant account. If you receive **403**, see [Forbidden / unverified](/knowledge-base/forbidden-403-unverified).
</Warning>

## Prerequisites

* Node.js 18+ with `fetch` (or undici)
* Recipient [party](/api/parties) and [counterparty](/api/counterparties) (or create them first)
* [Payment account](/api/payment-accounts) for the recipient with `operation_type=PAYOUT`
* Merchant **passcode** verification (required on payout execute)
* Server-side only — never expose Client Secret in the browser

## Flow overview

1. Authenticate → Bearer token
2. Ensure party / counterparty exist
3. `POST /v1/payment-accounts/create?operation_type=PAYOUT`
4. `POST /v1/sessions/payout` with `payout_account_id`, `amount`, `currency`
5. `POST /v1/executions/payout` with `session_id`, `payout_account_id`, `reference_id`, and `passcode`
6. `GET /v1/status/payout/:reference_id`

## Steps

<Steps>
  <Step title="Authenticate">
    ```javascript theme={null}
    const BASE_URL = "https://api.heydollr.app";

    const tokenRes = await fetch(`${BASE_URL}/v1/jwt/client/obtain/token`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        client_id: process.env.DOLLR_CLIENT_ID,
        client_secret: process.env.DOLLR_CLIENT_SECRET,
      }),
    });
    const tokenData = await tokenRes.json();
    const headers = {
      Authorization: `Bearer ${tokenData.access_token}`,
      "Content-Type": "application/json",
    };
    ```
  </Step>

  <Step title="Payment account (recipient wallet)">
    ```javascript theme={null}
    const account = await fetch(
      `${BASE_URL}/v1/payment-accounts/create?operation_type=PAYOUT`,
      {
        method: "POST",
        headers,
        body: JSON.stringify({
          account_name: "Beneficiary MTN Wallet",
          provider: "MTN_MOMO_LBR",
          method: "MTN_MOMO_LBR",
          party_id: partyId,
          country_code: "LR",
          insensitive_account_number: "231771234567",
        }),
      }
    ).then((r) => r.json());
    ```

    Use [MMO prediction](/api/predictions) to resolve `method` and `provider` from phone when possible.
  </Step>

  <Step title="Payout session">
    ```javascript theme={null}
    const session = await fetch(`${BASE_URL}/v1/sessions/payout`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        payout_account_id: account.id,
        amount: 100.0,
        currency: "USD",
      }),
    }).then((r) => r.json());
    ```

    The session response includes `expires_at` and `wallet_id` (the source wallet Dollr debits). You do **not** send `wallet_id` or `expires_at` on create.
  </Step>

  <Step title="Execute payout">
    ```javascript theme={null}
    const referenceId = crypto.randomUUID();

    const execution = await fetch(`${BASE_URL}/v1/executions/payout`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        session_id: String(session.id),
        payout_account_id: String(account.id),
        reference_id: referenceId,
        passcode: {
          token: {
            phone: "231771234567",
            code: process.env.MERCHANT_PASSCODE,
          },
          device: {
            name: "Payout Server",
            type: "SERVER",
            platform: "NODE",
            user_agent: "dollr-payout/1.0",
            client_ip: "203.0.113.10",
          },
        },
      }),
    }).then((r) => r.json());
    ```

    Persist `referenceId` in your database **before** awaiting the response. The `passcode` object is **required** — it verifies the merchant authorizing the payout.
  </Step>

  <Step title="Poll status">
    ```javascript theme={null}
    const status = await fetch(
      `${BASE_URL}/v1/status/payout/${referenceId}`,
      { headers }
    ).then((r) => r.json());

    console.log(status.status); // PENDING → PROCESSING → COMPLETED | FAILED
    ```

    Do not submit a second payout with a new `reference_id` while status is `PROCESSING`.
  </Step>
</Steps>

## Passcode payload

| Field                        | Required | Description                          |
| ---------------------------- | -------- | ------------------------------------ |
| `passcode.token.code`        | Yes      | Merchant passcode                    |
| `passcode.token.phone`       | No\*     | Phone tied to the passcode           |
| `passcode.token.user_id`     | No\*     | User identifier alternative to phone |
| `passcode.device.name`       | Yes      | Device label for audit               |
| `passcode.device.type`       | Yes      | e.g. `SERVER`, `MOBILE`              |
| `passcode.device.platform`   | Yes      | e.g. `NODE`, `IOS`                   |
| `passcode.device.user_agent` | Yes      | Client user agent string             |
| `passcode.device.client_ip`  | Yes      | Originating IP address               |

\* Provide `phone` or `user_id` on `token` as required by your merchant account setup.

## Obtain your payout passcode

1. Log in to [merchant.heydollr.app](https://merchant.heydollr.app)
2. Go to **Settings → Security** (or **Profile → Passcode**)
3. Create or reset your merchant passcode
4. Use the same phone in `passcode.token.phone` as registered in the portal

If passcode settings are not visible, your account may need full verification — see [Forbidden 403](/knowledge-base/forbidden-403-unverified). Troubleshooting: [Payout passcode errors](/knowledge-base/payout-passcode-error).

## Wallet funding

Payouts debit your Dollr wallet (`wallet_id` appears on the session response). Ensure sufficient balance before executing — see [Insufficient wallet balance](/knowledge-base/insufficient-wallet-balance).

## Related

* [Integration guide — Payout](/guides/integration#issue-a-payout)
* [Sessions & executions](/concepts/sessions-and-executions)
* [Forbidden 403](/knowledge-base/forbidden-403-unverified)
* [Collect with Node.js](/guides/collect-with-nodejs)
