Added

October 12th 2026

Added

[Wallet creation]
You can now create wallets in additional currencies directly through the API.

  • Added POST /wallet — create a client wallet in a requested currency_code. A client can hold at most one wallet per currency, so the call is idempotent: 201 returns the new wallet, 200 returns your existing wallet in that currency unchanged
  • Added POST /wallet/{'{user_token}'} — create a wallet for a user, with an optional fee_wallet_token naming one of your client wallets to pay the fees of payouts funded from the new wallet

Added

[Wallet details]
You can now look up a single wallet by its token.

  • Added GET /wallets/{'{wallet_token}'} — works for your own client wallets and your users' wallets
    The wallet object has also been extended:
  • type now distinguishes CLIENT_FUNDS and SUBLEDGER_FUNDS wallets alongside USER_FUNDS and MASSPAY_CARD
  • New fee_wallet_token (on the single-wallet endpoint) shows which client wallet pays the wallet's fees, when one is set
  • New wiring_instructions returns the wire/ACH deposit details of the bank account funding the wallet, when available. The object is the bank's own record passed through as-is, so keys vary by bank (e.g. Cross River returns accountNumber, routingNumber, accountType, title, currency, status) — treat the values as display text for your payer

Added

[Payout sender attribution]
You can now identify the true originator when initiating a payout on behalf of another party.

  • POST /payout/{'{user_token}'} accepts an optional sender_user_token referencing the sending user; when omitted, you (the client) remain the sender of record
  • The sender is echoed back on payout status responses, and transaction history entries include sender_user_token plus the sender's first_name/last_name or business_name
    Sender details are used for compliance, screening, and reporting, so the value must accurately reflect the true originator of the funds.

Added

[AgeChecker KYC]
Added support for the AgeChecker.Net age verification provider.

  • Added PUT /payout/user/{'{user_token}'}/kyc/agechecker
    Link an AgeChecker verification uuid to a user, and MassPay will fetch the verification status and verified buyer data (name, date of birth, address). Only an accepted verification is imported.

Added

[New webhook events]
The webhook event catalog now includes:

  • payin_deposit.returned and payin_deposit.return_completed — track deposit returns initiated via the deposit return endpoint
  • file_upload_req.created, file_upload_req.status, and file_upload_req.verified — follow the lifecycle of file upload requests
    Subscribe via POST /payout/account/webhooks.

Added

[OAuthGrant attribute]
Added OAuthGrant as a user attribute type, supporting payout destinations authorized through a provider OAuth flow (e.g. Cash App).

Improved

[File upload request lifecycle]
File upload requests now report rejections. When MassPay operations rejects uploaded files, a replacement request is opened automatically for the same requirement.

  • New status field on file upload request responses: pending, uploaded, verified, or rejected
  • New is_rejected, rejection_reason, and time_rejected fields
  • New replacement_file_upload_token (and replacement_file_upload_link on the single-request endpoint) pointing to the replacement request
  • A rejection fires file_upload_req.status (with event_type file_upload_req_rejected) and file_upload_req.created for the replacement
    A rejected request no longer counts toward any requirement.

Improved

[Payout initiation]
POST /payout/{'{user_token}'} validation and documentation have been tightened.

  • destination_token is no longer required — when omitted, the last destination used for the user applies (destination_currency_code and source_token remain required)
  • Token parameters now accept both bare UUIDs and prefixed forms (usr_, dest_, attr_set_, clnt_wlt_, etc.); examples and patterns updated throughout
  • client_transfer_id uniqueness is now documented: a value already carried by another payout is rejected with 400 and Duplicate client id. Existing payout token: payout_<uuid> in error.details. By default a value becomes reusable once the earlier payout is ERROR or EXPIRED (or a PENDING quote past expiration); accounts configured for strict uniqueness never allow reuse
  • expiration is now documented as UTC RFC 3339 with millisecond precision (e.g. 2026-08-22T17:40:46.881Z)

Improved

[Status refresh and webhooks]
Clarified force_status_update behavior on payout status and pay-in status: when the partner answers, the corresponding payout.status / payin.status webhook is sent whether or not the status changed. Never set it from inside a webhook handler — the handler would send itself the next event.

Improved

[Transaction history]

  • New RETURNED status for returned transactions
  • Cancelled payouts now appear twice in history: the original entry with CANCELLED_AT_LATER_TIME at creation time, and a second CANCELLED entry at the time the cancellation happened and the funds came back. The payout itself has the single status CANCELLED
  • The ledger gained a payin_deposit_return entry type

Improved

[Payout reversals]
Reversal status responses now include a status_reason with the reason code when available (e.g. R01 - Insufficient Funds).

Improved

[Business enrollment]
POST /business accepts a new partner_pays_transaction_fees flag (default false): when true, the enrolling partner assumes the transaction fees incurred by the business.

Improved

[Documentation cleanup]

  • POST /payout/account/api-key: documented that a new API key becomes valid a few seconds after the response (rarely a few minutes), not instantly
  • Webhook secret documentation now spells out the signature scheme: HMAC-SHA512 over <t>|<body>, delivered in the X-MassPay-Signature header
  • Wallet transfers: documented that amount accepts up to 5 decimal places
  • Corrected webhook listing examples to show real event_types values

Changed

[Webhook listing response]
Breaking: in the GET /payout/account/webhooks response, the webhook_token field is renamed to token. The value is unchanged — it is still the token returned as webhook_token when the webhook was created, and the value to use when deleting a webhook. Update any code reading webhook_token from the listing.