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.
Use a payout beneficiary when:
Pass recipient inline on the payout request instead when:
Both flows are supported. payoutBeneficiaryId and recipient are mutually exclusive — supply exactly one on Initiate Payout.
A beneficiary uses the same recipient shape as a payout. The type discriminator selects the destination:
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.
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.
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.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.
rejected with an AML-specific reason.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.
A beneficiary moves through this lifecycle:
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.
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).
Response (201):
Poll Retrieve Payout Beneficiary (or subscribe to webhooks once available) until status is approved before referencing the beneficiary on a payout.
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.
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:
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:
Once the beneficiary is approved, reference it from Initiate Payout using payoutBeneficiaryId instead of recipient:
payoutBeneficiaryId and recipient are mutually exclusive — supply one or the other. A payout that references a non-approved beneficiary is rejected with 422.
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.
A beneficiary held on a partial account-name match looks like this when retrieved:
The merchant compares recipient.accountHolderName against verifications.accountVerification.returnedAccountHolderName and signals:
For a transient failure:
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.
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:
409 is telling you the beneficiary already exists — store the returned ID and use it.approved. Don’t reference a pending_review, failed, or rejected beneficiary on a payout. The payout will be rejected.returnedAccountHolderName on partial matches. Surface it in your UI alongside the submitted name so a human can accept / reject with full context.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.