For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
StatusDashboard
GuidesAPI ReferenceHelp Center
GuidesAPI ReferenceHelp Center
  • Getting Started
    • Introduction to CrissCross
    • Account Setup
    • Authentication
    • Conventions
    • Testing Connectivity
    • Understanding Responses
    • Error Codes
  • Collect
    • Getting Started
    • Webhooks
  • Exchange
    • Getting Started
    • Currencies & Conventions
    • Funding
    • Trading
    • Sandbox
  • Payouts
    • Getting Started
    • Single Payouts
    • Bulk Payouts
    • Tracking Payouts
    • Mobile Number Verification
    • Bank Account Verification
    • Payout Beneficiaries
LogoLogo
StatusDashboard
On this page
  • When to Use a Payout Beneficiary
  • Supported Destination Types
  • Pre-acceptance Checks
  • Account verification
  • AML pre-screening
  • Lifecycle
  • Creating a Beneficiary
  • Mobile Money
  • Bank Account
  • Duplicate Handling
  • Using a Beneficiary on a Payout
  • Signals — Manual Lifecycle Transitions
  • Listing and Retrieval
  • Best Practices
Payouts

Payout Beneficiaries

Register recipients once, then reuse them on every payout
Was this page helpful?
Previous

Bank Transfers

Send payouts to bank accounts
Next
Built with

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.

Supported Destination Types

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

typeVerified against
bank_accountInternal format rules (e.g. NUBAN check-digit for NGA) followed by a partner bank-verification provider.
mobile_moneyThe mobile-money carrier confirms the MSISDN and registered wallet name.
cashNo network-side verification (the destination is a cash agent collection); the beneficiary still runs AML pre-screening.
institution_walletNo 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:

StatusMeaningUsable on a payout?
pending_reviewCreated 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
approvedAccount verification and AML pre-screening both cleared; ready to use.Yes
rejectedDeclined by signal, by AML, or after exhausting verification retries. Terminal.No
failedVerification 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

1{
2 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
3 "merchantReference": "BENE-2026-001",
4 "recipient": {
5 "type": "mobile_money",
6 "country": "UGA",
7 "operator": "mtn",
8 "phoneNumber": "+256700000000",
9 "name": "Jane Doe"
10 }
11}

Response (201):

1{
2 "payoutBeneficiaryId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
3 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
4 "merchantReference": "BENE-2026-001",
5 "status": "pending_review",
6 "recipient": {
7 "type": "mobile_money",
8 "country": "UGA",
9 "operator": "mtn",
10 "phoneNumber": "+256700000000",
11 "name": "Jane Doe"
12 },
13 "verifications": {
14 "accountVerification": {
15 "state": "PENDING",
16 "attempts": 0
17 },
18 "amlScreening": {
19 "state": "PENDING",
20 "attempts": 0
21 }
22 },
23 "createdAt": "2026-05-21T10:14:00Z",
24 "updatedAt": "2026-05-21T10:14:00Z"
25}

Poll Retrieve Payout Beneficiary (or subscribe to webhooks once available) until status is approved before referencing the beneficiary on a payout.

Bank Account

1{
2 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
3 "merchantReference": "BENE-2026-002",
4 "recipient": {
5 "type": "bank_account",
6 "country": "NGA",
7 "bankCode": "044",
8 "accountNumber": "0123456789",
9 "accountHolderName": "Jane Doe"
10 }
11}

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:

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

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:

1{
2 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
3 "merchantReference": "BENE-2026-001-retry",
4 "allowDuplicate": true,
5 "recipient": { "...": "..." }
6}

Using a Beneficiary on a Payout

Once the beneficiary is approved, reference it from Initiate Payout using payoutBeneficiaryId instead of recipient:

1{
2 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
3 "merchantReference": "PAYOUT-2026-001",
4 "destinationValue": {
5 "minorAmount": 5000000,
6 "currency": "UGX"
7 },
8 "paymentMethodId": "mobilemoney",
9 "paymentLocation": "UGA",
10 "payoutBeneficiaryId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
11 "sender": {
12 "fullName": "Acme Ltd",
13 "identity": "businessRegistrationNumber",
14 "identityNumber": "RC-1234567"
15 }
16}

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.

SignalEffect
acceptClears 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.
rejectDeclines a PARTIAL_MATCH account-name hold. Terminal rejected. Not valid against an AML REVIEW hold.
retryRe-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:

1{
2 "payoutBeneficiaryId": "7c2a9e15-3b48-4f01-bc23-d5e9af6b1c84",
3 "merchantId": "550e8400-e29b-41d4-a716-446655440000",
4 "merchantReference": "BENE-2026-014",
5 "status": "pending_review",
6 "recipient": {
7 "type": "bank_account",
8 "country": "NGA",
9 "bankCode": "044",
10 "accountNumber": "0123456789",
11 "accountHolderName": "Jane A Doe"
12 },
13 "verifications": {
14 "accountVerification": {
15 "state": "PARTIAL_MATCH",
16 "attempts": 1,
17 "provider": "flutterwave",
18 "returnedAccountHolderName": "JANE ANNE DOE"
19 },
20 "amlScreening": {
21 "state": "CLEARED",
22 "attempts": 1
23 }
24 },
25 "createdAt": "2026-05-21T10:14:00Z",
26 "updatedAt": "2026-05-21T10:14:30Z"
27}

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

1{
2 "signal": "accept",
3 "reason": "Confirmed same person — middle name expansion is expected"
4}

For a transient failure:

1{
2 "signal": "retry",
3 "reason": "Provider was rate-limited at first attempt"
4}

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:

1{
2 "payoutBeneficiaries": [
3 { "payoutBeneficiaryId": "f47ac10b-…", "...": "..." }
4 ]
5}

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.