> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Payout Beneficiaries

A **payout beneficiary** is a pre-screened payout recipient. You register the destination once, CrissCross runs two independent pre-acceptance checks against it — account verification with the receiving network and AML pre-screening — and from then on you reference the beneficiary by `payoutBeneficiaryId` on every payout instead of re-sending the full account details.

This keeps sensitive account data out of repeat requests, surfaces invalid or sanctioned destinations before money moves, and lets you build dashboards over the recipients a merchant has approved.

## When to Use a Payout Beneficiary

Use a payout beneficiary when:

* You expect to pay the same recipient more than once
* You want destination validity confirmed **before** initiating a payout (e.g. before debiting the merchant balance)
* You want a stable, audit-friendly handle for a recipient

Pass `recipient` inline on the payout request instead when:

* It's a one-off payout you don't expect to repeat
* You don't need (or want) pre-validation before initiating

Both flows are supported. `payoutBeneficiaryId` and `recipient` are mutually exclusive — supply exactly one on [Initiate Payout](/api-reference/payouts/payouts/payouts/initiate-payout).

## Supported Destination Types

A beneficiary uses the same `recipient` shape as a payout. The `type` discriminator selects the destination:

| `type`               | Verified against                                                                                                         |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `bank_account`       | Internal format rules (e.g. NUBAN check-digit for NGA) followed by a partner bank-verification provider.                 |
| `mobile_money`       | The mobile-money carrier confirms the MSISDN and registered wallet name.                                                 |
| `cash`               | No network-side verification (the destination is a cash agent collection); the beneficiary still runs AML pre-screening. |
| `institution_wallet` | No network-side verification; the beneficiary still runs AML pre-screening.                                              |

Both `bank_account` and `mobile_money` flow through the same internal `AccountValidationService`, so they share the same lifecycle, the same `verifications.accountVerification` surface, and the same `accept` / `reject` signal behaviour.

## Pre-acceptance Checks

Every beneficiary runs two independent checks while it is in `pending_review`. Both must clear before the beneficiary transitions to `approved`; either can independently block.

### Account verification

CrissCross verifies the destination with the receiving network. For mobile money that's the carrier confirming the MSISDN belongs to a live wallet under the supplied account name; for bank accounts it's the partner bank-verification provider (e.g. Flutterwave for NGA Access Bank). The outcome surfaces on `verifications.accountVerification`:

* `VERIFIED` — the network confirmed the destination and the returned name matched.
* `PARTIAL_MATCH` — the network confirmed the destination but returned an account holder name that **differs** from the merchant-submitted name. The differing name is exposed as `returnedAccountHolderName` so the merchant can compare them side-by-side. The beneficiary stays in `pending_review` until cleared via `signal`.
* `NOT_VERIFIED` — the network could not locate the account, or rejected it. The beneficiary moves to `rejected`.
* `PENDING` — verification has not completed yet.

### AML pre-screening

In parallel, CrissCross screens the beneficiary's name (and identifying details where supplied) against sanctions lists (OFAC, UN, EU, UK), PEP lists, and internal risk lists.

* A **clear-cut hit** moves the beneficiary to `rejected` with an AML-specific reason.
* A **possible hit** holds the beneficiary in `pending_review` with `verifications.amlScreening.state: REVIEW`. **CrissCross's compliance team reviews the hold in our screening provider's tooling** and clears or declines it there — the decision is not merchant-actionable, and `accept` / `reject` against an AML hold returns `422`. The merchant simply waits; the beneficiary moves to `approved` once compliance clears it, or to `rejected` if compliance declines.

Use `signal: retry` to re-run both checks if either had a transient error.

## Lifecycle

A beneficiary moves through this lifecycle:

```mermaid
stateDiagram-v2
    [*] --> pending_review: Create
    pending_review --> approved: Verification + AML both clear
    pending_review --> rejected: Network rejects, AML rejects, or signal reject
    pending_review --> failed: Verification or screening errored
    failed --> pending_review: signal retry
    rejected --> [*]
    approved --> [*]
```

| Status           | Meaning                                                                                                                                                                                                                                                                                                                                | Usable on a payout? |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `pending_review` | Created and awaiting verification / AML pre-screening, **or** held on a partial account-name match (merchant-actionable via `accept` / `reject`), **or** held on AML compliance review (resolved by CrissCross's compliance team in our screening provider's tooling — not merchant-actionable). Inspect `verifications` to see which. | No                  |
| `approved`       | Account verification and AML pre-screening both cleared; ready to use.                                                                                                                                                                                                                                                                 | **Yes**             |
| `rejected`       | Declined by signal, by AML, or after exhausting verification retries. Terminal.                                                                                                                                                                                                                                                        | No                  |
| `failed`         | Verification or screening errored. Recover with `signal: retry`.                                                                                                                                                                                                                                                                       | No                  |

Beneficiaries are **immutable** after creation. To change any field — MSISDN, account number, account name, anything — create a new beneficiary. There is intentionally no `PATCH` / `PUT`.

## Creating a Beneficiary

`POST /v1/payout-beneficiaries`

The request body carries `merchantId`, a merchant-side `merchantReference` (persisted and round-tripped on every read), and the `recipient` (same shape as on Initiate Payout).

### Mobile Money

```json
{
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "BENE-2026-001",
  "recipient": {
    "type": "mobile_money",
    "country": "UGA",
    "operator": "mtn",
    "phoneNumber": "+256700000000",
    "name": "Jane Doe"
  }
}
```

Response (`201`):

```json
{
  "payoutBeneficiaryId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "BENE-2026-001",
  "status": "pending_review",
  "recipient": {
    "type": "mobile_money",
    "country": "UGA",
    "operator": "mtn",
    "phoneNumber": "+256700000000",
    "name": "Jane Doe"
  },
  "verifications": {
    "accountVerification": {
      "state": "PENDING",
      "attempts": 0
    },
    "amlScreening": {
      "state": "PENDING",
      "attempts": 0
    }
  },
  "createdAt": "2026-05-21T10:14:00Z",
  "updatedAt": "2026-05-21T10:14:00Z"
}
```

Poll [Retrieve Payout Beneficiary](/api-reference/payouts/payouts/payout-beneficiaries/get-payout-beneficiary) (or subscribe to webhooks once available) until `status` is `approved` before referencing the beneficiary on a payout.

### Bank Account

```json
{
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "BENE-2026-002",
  "recipient": {
    "type": "bank_account",
    "country": "NGA",
    "bankCode": "044",
    "accountNumber": "0123456789",
    "accountHolderName": "Jane Doe"
  }
}
```

Bank accounts run the same verification path as mobile money: any applicable internal format rules first (e.g. NUBAN check-digit for NGA), then the partner bank-verification provider. The verification outcome surfaces on `verifications.accountVerification` exactly like mobile money.

## Duplicate Handling

The API soft-blocks duplicate creates. If you submit a beneficiary whose identifying details match one you already own, you get back `409 Conflict` with the existing `payoutBeneficiaryId`:

```json
{
  "message": "A PayoutBeneficiary with the same identifying details already exists",
  "existingPayoutBeneficiaryId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
```

In almost every case the right thing to do is reuse the returned ID. If you genuinely need a second record (for example, the previous one was rejected and you want a fresh verification attempt under a new ID), resend with `allowDuplicate: true`:

```json
{
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "BENE-2026-001-retry",
  "allowDuplicate": true,
  "recipient": { "...": "..." }
}
```

## Using a Beneficiary on a Payout

Once the beneficiary is `approved`, reference it from [Initiate Payout](/api-reference/payouts/payouts/payouts/initiate-payout) using `payoutBeneficiaryId` instead of `recipient`:

```json
{
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "PAYOUT-2026-001",
  "destinationValue": {
    "minorAmount": 5000000,
    "currency": "UGX"
  },
  "paymentMethodId": "mobilemoney",
  "paymentLocation": "UGA",
  "payoutBeneficiaryId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "sender": {
    "fullName": "Acme Ltd",
    "identity": "businessRegistrationNumber",
    "identityNumber": "RC-1234567"
  }
}
```

`payoutBeneficiaryId` and `recipient` are mutually exclusive — supply one or the other. A payout that references a non-`approved` beneficiary is rejected with `422`.

## Signals — Manual Lifecycle Transitions

`POST /v1/payout-beneficiaries/{payoutBeneficiaryId}/signal`

Signals let the merchant clear a **name-mismatch** hold or re-run the pre-acceptance checks after a transient failure.

`accept` and `reject` are merchant-facing decisions about the **account-name verification only** (the data the merchant submitted, compared to what the receiving network returned). They are **not** valid against an AML compliance hold.

**AML screening decisions are made by CrissCross's compliance team in our screening provider's tooling — never by the merchant.** When a beneficiary is in compliance review (`verifications.amlScreening.state: REVIEW`), the merchant waits; sending `accept` or `reject` against that hold returns `422 INVALID_SIGNAL_FOR_STATUS`. `retry` remains valid and re-runs both checks.

| Signal   | Effect                                                                                                                                                                                                           |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accept` | Clears a `PARTIAL_MATCH` account-name hold (the merchant confirms the small name difference is acceptable). If AML is already clear the beneficiary moves to `approved`. Not valid against an AML `REVIEW` hold. |
| `reject` | Declines a `PARTIAL_MATCH` account-name hold. Terminal `rejected`. Not valid against an AML `REVIEW` hold.                                                                                                       |
| `retry`  | Re-runs **both** account verification and AML pre-screening for a beneficiary in a recoverable state. Valid in any non-terminal state.                                                                           |

A beneficiary held on a partial account-name match looks like this when retrieved:

```json
{
  "payoutBeneficiaryId": "7c2a9e15-3b48-4f01-bc23-d5e9af6b1c84",
  "merchantId": "550e8400-e29b-41d4-a716-446655440000",
  "merchantReference": "BENE-2026-014",
  "status": "pending_review",
  "recipient": {
    "type": "bank_account",
    "country": "NGA",
    "bankCode": "044",
    "accountNumber": "0123456789",
    "accountHolderName": "Jane A Doe"
  },
  "verifications": {
    "accountVerification": {
      "state": "PARTIAL_MATCH",
      "attempts": 1,
      "provider": "flutterwave",
      "returnedAccountHolderName": "JANE ANNE DOE"
    },
    "amlScreening": {
      "state": "CLEARED",
      "attempts": 1
    }
  },
  "createdAt": "2026-05-21T10:14:00Z",
  "updatedAt": "2026-05-21T10:14:30Z"
}
```

The merchant compares `recipient.accountHolderName` against `verifications.accountVerification.returnedAccountHolderName` and signals:

```json
{
  "signal": "accept",
  "reason": "Confirmed same person — middle name expansion is expected"
}
```

For a transient failure:

```json
{
  "signal": "retry",
  "reason": "Provider was rate-limited at first attempt"
}
```

Returns `200` with the updated beneficiary. Sending a signal that isn't valid for the current status (e.g. `retry` on an already-`approved` beneficiary) returns `422`.

## Listing and Retrieval

* `GET /v1/payout-beneficiaries?merchantIds=<id>[,<id>...]&status=<status>` — list beneficiaries across one or more merchants the caller has access to, optionally filtered by status.
* `GET /v1/payout-beneficiaries/{payoutBeneficiaryId}` — fetch a single beneficiary. Returns `404` if it doesn't exist or isn't owned by the caller.

List responses are an envelope of `payoutBeneficiaries`:

```json
{
  "payoutBeneficiaries": [
    { "payoutBeneficiaryId": "f47ac10b-…", "...": "..." }
  ]
}
```

## Best Practices

* **Reuse, don't recreate.** A duplicate `409` is telling you the beneficiary already exists — store the returned ID and use it.
* **Wait for `approved`.** Don't reference a `pending_review`, `failed`, or `rejected` beneficiary on a payout. The payout will be rejected.
* **Treat beneficiaries as immutable.** Account details changed? Create a new beneficiary and update your local mapping.
* **Show `returnedAccountHolderName` on partial matches.** Surface it in your UI alongside the submitted name so a human can `accept` / `reject` with full context.
* **Don't gate your UX on AML compliance reviews.** Compliance reviews can take hours to days, are decided by CrissCross's compliance team in our screening provider's tooling, and are not merchant-actionable. Show "Under compliance review — we'll update you" rather than offering Accept/Decline buttons.
* **Use `signal: retry` for transient failures.** If verification errored because the provider was down, `retry` re-runs both checks without creating a new record.

For full request/response details, see the [Payout Beneficiaries API Reference](/api-reference/payouts/payouts/payout-beneficiaries/create-payout-beneficiary).