docs
/
AI skills

Payouts

Pay people the earnings in their wallet (affiliates, hosts, sellers) by PayPal or bank transfer. Set the payout rules, add and check payout methods, credit and hold earnings, take a payout request from pending to completed, build ACH bank files, and handle failures, returns, freezes and limits. Every email each step sends, read from the code.

A wallet (wallet) belongs to one person: a customer, an affiliate or a host. Wallets are opened for them when they first earn (affiliate commission; see Affiliate program), when they open their account's wallet, or by staff. Only the earnings balance can be paid out. Store credit and reward points in the same wallet are only spent; see Store credit, reward points and wallets.

available = balance − held − reserved

A payout request reserves its amount. It is a payout record (appengine finance/payout.service.ts):

pending → approved → processing → completed
   └──────────┴──────────┴──→ failed      (pending, approved, processing)
   └──────────┴──→ cancelled              (pending, approved)

Payouts are sent two ways only: PayPal (the PayPal Payouts API, with the org's PayPal connection) and bank (an ACH file staff take to the bank). Other method types in old records (Venmo, Cash App, check, wire…) can't be added or sent (validatePayoutMethodInput, 1661). Tested: a Venmo method was refused: "Unsupported payout method "venmo". This platform pays by paypal or bank."

A payout pays real money to a real account. PayPal sends the moment it is processed. Never approve, process, complete or batch a payout without the owner's yes on that payout.

Ask first

  • Who approves? A person every time, everything automatically, or automatically under an amount.
  • How are people paid? PayPal, bank (ACH), or both. For bank: the business's ACH sender details from its bank.
  • Minimum payout? Set per wallet. There is no org-wide minimum.
  • Which payout? The payout number, who, how much, which method, and whether the method is verified.

Steps

1. Finance › Payout rules

Studio: Finance (/finance), tab Payout rules (websitemint payout-rules.tsx). Saved as a setting with type: "payout_config":

FieldOn screenWhat it does
approval.modeSomebody approves every payout / Approve everything automaticallymanual or auto.
approval.autoUnder…except anything underIn manual mode, payouts at or under this amount are approved automatically.
scheduleWhen payouts are raisedmanual (Only when asked), daily, weekly, biweekly, monthly.

Nothing runs the schedule by itself (runScheduledPayouts, 1090, has no timer). Run a payout cycle now (POST finance/payouts/run-schedule { force: true }) raises one payout for each active wallet with money available, a working method, nothing in flight and at least its own minimum. Wallets set to manual are skipped. It pays nobody by itself.

Stop: auto approval sends payouts to processing without a person. Show the rule and wait for yes.

Check: reopen Payout rules; it shows what was saved.

2. Payout methods

The holder adds their own in My Account (client/finance/payout-methods; see Customer account portal). Staff can add one on the wallet (websitemint payout-method-manager.tsx; POST finance/wallets/<walletId>/payout-methods). Staff need the Storefront › Finance permission for this (payout-method-access.guard.ts).

TypeBodyChecked
paypal{ type: "paypal", label?, isDefault?, paypal: { email } }A valid email.
bank{ type: "bank", label?, isDefault?, bank: { bankName?, accountType: checking | savings, routingNumber, accountNumber, accountHolderName } }Routing check digit, account of 4 or more, holder name. The numbers are sealed; only the last four can be read again.

GET finance/payout-method-options says which types are on, whether bank details can be stored, and whether Plaid is set up. With Plaid, the holder links their bank (…/payout-methods/plaid/link-token, then …/plaid/exchange) and the method is verified.

A method is pending, verified, failed or disabled. An unverified method can still be paid. Its verified flag is copied onto each payout so the approver sees it. A disabled one is refused. Adding a method emails the holder payout-method-added.

Tested: a PayPal method added by staff showed pending, not verified, "added by staff".

Stop: this emails the holder. Bank details come from the holder, never from a guess.

Hands on: the method id. Check: GET finance/wallets/<walletId>/payout-methods lists it.

3. Finance › Wallets: credit, hold and freeze

Tab Wallets (/finance?openWallet=<sk>). With call_appengine (finance/payout.controller.ts):

CallBodyNotes
POST finance/wallets/get-or-create{ ownerType, ownerId, email, name }One wallet per owner.
POST finance/wallets/<id>/credit{ amount, type, reference, description }type: earning, tip, bonus, commission, referral, refund, adjustment. Emails the holder wallet-credited.
POST finance/wallets/<id>/debit{ amount, type, reference, description }type: payout, fee, chargeback, adjustment.
POST finance/wallets/<id>/hold{ reason, amount, reference?, expiresAt?, notes? }reason: dispute, chargeback, security, deposit, pending_review, other. Moves money from available to held.
POST finance/wallets/<id>/release/<holdIndex>By the hold's position in holds[], from 0. Read the wallet first.
POST finance/wallets/<id>/freeze{ reason }Stops credits, debits, payouts and store credit changes. "Wallet is frozen: . Unfreeze it first." (tested)
POST finance/wallets/<id>/unfreeze
PUT finance/wallets/<id>/payout-settings{ frequency, minimumAmount, dayOfWeek, dayOfMonth }frequency: manual, daily, weekly, monthly. minimumAmount is also the wallet's minimum payout. dayOfWeek 0–6, dayOfMonth 1–28.

expiresAt on a hold is only recorded. Release holds by hand.

Stop before a credit (it emails the holder and books the amount as owed), a debit, a hold or a freeze.

Tested: credit $50, hold $5; a request for $48 was refused ("Only 45.00 of earnings…"); release worked.

Check: GET finance/wallets/<id> shows balance, heldBalance, reservedBalance and holds[].

4. The request

The holder presses Request Payout in My Account (POST client/finance/payouts/request { amount, methodId?, notes? }), or staff request for them (POST finance/wallets/<walletId>/request-payout, same body). Checked in order (requestPayout, 854):

  1. The wallet is active.
  2. The amount is no more than available.
  3. The wallet's limits.minimumPayout, maximumPayout and dailyLimit.
  4. A method: the one named, else the default, else the first. "No payout method configured. Please add a payout method first." (tested)

The request saves a copy of where the money goes, reserves the amount, emails the holder payout-requested, and is approved at once when the payout rules say so.

Hands on: the payout id and payoutNumber. Check: it is pending (or approved), and the wallet's reservedBalance went up by the amount (tested).

5. Finance › Payouts: approve, process, complete

Tab Payouts (/finance?openPayout=<sk>; websitemint payout-list.tsx). Show the owner the payout first: who, how much, the method, verified or not.

ButtonCallFromWhat happensEmail
ApprovePOST finance/payouts/<id>/approve { approvedBy, notes }pendingapproved. Tested; a second approve: "Payout is not pending".payout-approved
ProcessPOST finance/payouts/<id>/processpending, approvedPayPal: sent now. Bank: queued for the next ACH file. Either way processing.payout-submitted
CompletePOST finance/payouts/<id>/complete { externalReference }approved, processingcompleted: the balance goes down, the reserve is removed, a ledger line is written and it is booked.payout-completed
Mark FailedPOST finance/payouts/<id>/fail { reason, code }pending, approved, processingfailed, reserve removed. Tested.payout-rejected
CancelPOST finance/payouts/<id>/cancel { reason }pending, approvedcancelled, reserve removed. Tested; a second cancel: "Payout cannot be cancelled".payout-rejected

Stop: before every one of these. Process sends money by PayPal at once, and Complete records money as paid.

PayPal payouts complete by themselves: PayPal's webhook (PAYMENT.PAYOUTS-ITEM.SUCCEEDED, FAILED, RETURNED…) settles or fails them (payout-monitor.service.ts). If one is stuck in processing, ask PayPal again with POST finance/payouts/<id>/refresh-status, or POST finance/payouts/sweep for all of them. Don't press Complete on a PayPal payout.

Use Complete by hand only for money paid outside the platform, with the bank or PayPal reference as externalReference.

POST finance/batch/payouts { walletIds, minAmount? } raises a request for each wallet's whole balance (it doesn't approve or send; a wallet with anything held or reserved is refused and skipped). GET finance/stats/payouts gives the totals.

Check: the payout's history shows each step, and the wallet's balance went down only at completion.

6. Finance › ACH runs: bank payouts

Tab ACH runs (websitemint ach-runs.tsx; payout-batch.service.ts).

Sender details (PUT finance/payout-ach-config), from the business's bank. A file can't be built without the first three:

FieldOn screen
originatorNameCompany name (up to 16 characters)
originatorIdCompany ID (usually 1 + the EIN, 10 characters)
odfiRoutingNumberYour bank's routing number
immediateDestinationNameYour bank's name (optional)
companyEntryDescriptionEntry description, e.g. PAYOUT (optional)
notifyEmailTell this email when a file is ready (optional)

Then, per run:

ButtonCallWhat happens
(list)GET finance/payout-batches/eligibleApproved or processing bank payouts not yet in a file.
Build the filePOST finance/payout-batches { payoutIds?, effectiveDate? }A NACHA file. The payouts go to processing. Emails payout-batch-ready to notifyEmail.
(download)GET finance/payout-batches/<id>/fileThe file to upload at the bank.
It is with the bankPOST finance/payout-batches/<id>/sentBooks the payouts.
The bank confirmed itPOST finance/payout-batches/<id>/settleCompletes every payout in it.
It bouncedPOST finance/payout-batches/<id>/items/<payoutId>/returnFails that payout (ach_return): its reserve is released (the balance was never taken down), its booking is turned around, and the holder gets payout-returned.

A batch reads Waiting to go to the bank, With the bank, Settled, Partly returned or Cancelled.

Stop: before building, sending and settling. Uploading the file at the bank is a person's job.

Record a bounce before settling the batch. After settling, the payout is completed and can no longer be failed (payout-batch.service.ts 580, payout.service.ts 1432).

(Code only: no ACH file was built in testing.)

Books

Books › Posting choices › "How wallet payouts are booked" (walletPayouts, appengine business-made/ledger/posting-choices.ts):

ChoiceCreditsPayouts
liability (default)Booked as an expense owed to the holderPay the debt off
expenseNothingBooked as an expense when the money leaves
noneNothingNothing

A payout is booked once: when its ACH file goes to the bank, or when it completes. A return turns it around. Holds and freezes post nothing. See Finance and Wallets, payouts and payments.

Emails a payout sends

All go to the wallet holder, except payout-batch-ready:

TemplateWhen
wallet-creditedStaff credit the wallet
payout-method-addedA method is added
payout-requestedA payout is requested
payout-approvedApprove
payout-submittedProcess (PayPal sent, or bank queued)
payout-completedComplete, a PayPal success, or a settled ACH file
payout-rejectedFail or cancel
payout-returnedAn ACH return
payout-batch-readyAn ACH file is built (to notifyEmail)

When it's done

  • The payout reads completed with an external reference, the wallet's balance is down by the amount and its reserve is back to 0.
  • Or it reads failed / cancelled with a reason, and the reserve is removed.
  • It is in Books under the posting choice.

What it can't do yet

  • No timer. The schedule, the wallet's own frequency and day, and the PayPal sweep run only when someone asks.
  • Daily and weekly limits. dailyLimit is checked but the day's total is never counted up, and weeklyLimit isn't used (payout.service.ts 902–906).
  • Frequency choices differ. The wallet record and Payout rules offer biweekly (and the record instant), but a wallet's own payout settings accept only manual, daily, weekly and monthly (wallet-controls.ts 17).
  • No Venmo, Cash App, check or wire.
  • No undo after an ACH settle (step 6).

Don'ts

  • Don't approve, process, complete, batch or settle a payout without a yes on that payout.
  • Don't press Complete on a PayPal payout: PayPal settles it.
  • Don't settle an ACH file before the bank has confirmed every entry.
  • Don't release a hold by index without reading holds[] first.
  • Don't type bank details for a holder from a message or a guess. The holder adds them, or Plaid verifies them.
  • Don't offer to pay out store credit or reward points.