Appearance
How payouts work
Who moves the money, what Zay checks, and how a payout ends.
Do it: Get started · Look it up: Payouts, Payout events
You send, Zay confirms
A payout represents a USDC transfer to a user. You send the transfer on-chain yourself, to the user's wallet_address, then report it, passing the transaction_hash. The payout is created in pending.
Zay then looks for the matching USDC deposit in the user's wallet — same transaction hash, same amount. When it finds one, the payout moves to received and Zay sends payout.received. You never report the outcome yourself.
Pay a user only after user.onboarding_completed — see How onboarding works. Zay does not stop you from reporting a payout earlier, but there is no wallet to confirm it against.
The lifecycle
pending ──matching deposit found──────────────→ received
└─────deposit mismatched, or none in 48 hours──→ expiredpending— reported, not yet confirmed.received— terminal success. Zay confirmed the transfer and sentpayout.received.expired— terminal failure. Either the deposit did not match the report (wrong amount, token, or a failed transaction), or no matching deposit arrived within 48 hours. Zay sentpayout.expired, with the cause inreason.
received and expired are final — a payout never leaves them.
Confirmation is not live yet
Zay does not yet confirm deposits to the user's wallet, on staging or in production. Until it does, every reported payout stays pending and moves to expired after 48 hours, with payout.expired and the reason deposit not confirmed within the verification window. The lifecycle above is the contract you integrate against; only the received branch is not reachable yet.