Send push-to-card payouts

A push-to-card payout sends funds from your platform to an account holder's debit card. You create it with POST/v1/transfers: a Transfer with a debit card as the destination and no source. The payout settles asynchronously; the card network posts the funds, usually within minutes.

This page covers card payouts only. For ACH payouts and the shared payout model, see Pay out to an account.

Prerequisites

RequirementDetails
Push-to-card enabledIn live mode, Frame turns card payouts on per merchant. Until it does, every live card payout returns 422. Contact Frame to enable it. In sandbox, card payouts are always on.
Payout billing agreementAn active billing agreement covering the payout category.
Recipient accountAn individual Account with the kyc and card_receive capabilities active.
Destination cardA Visa or Mastercard debit card saved to the recipient account as a PaymentMethod.
CurrencyUSD only.

To get an account to this state, use Run onboarding and request kyc and card_receive.

1. Check the account

Call GET/v1/accounts/{id} and confirm kyc and card_receive are active in capabilities.

A pending capability means the account holder still has steps to finish in onboarding. A disabled capability was switched off on purpose; onboarding doesn't lift it.

2. Check the card can receive funds

Call GET/v1/payment_methods/{id} on the destination card, or GET/v1/accounts/{id}/payment_methods to pick one, and read card.instant_available.

Show a "pay out to card" option only when it is true. When it is false, card.instant_unavailable_reason says why and card.instant_steer_to names the rail to offer instead; the PaymentMethod reference lists the values. A payout to a card that fails this check is rejected or fails after creation.

3. Create the payout

Call POST/v1/transfers with the card in destination_payment_method_id, no source_payment_method_id, and a reference. The card selects the push-to-card rail; there is no rail, speed or transaction-type field to set.

curl -X POST https://api.framepayments.com/v1/transfers \
  -H "Authorization: Bearer $FRAME_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "account_id": "acct_...",
    "destination_payment_method_id": "pm_...",
    "reference": "payout-2026-11-withdrawal-4471"
  }'

A created payout returns 201 with the transfer in pending and the ID of the payout behind it in payout. The POST/v1/transfers reference shows the full request and the card payout response.

4. Track the payout

201 means Frame accepted the payout, not that the funds moved. Treat the payout as paid only on transfer.succeeded.

Listen for transfer.succeeded and transfer.failed. transfer.succeeded means the network accepted the push; the issuer posts the funds, usually within minutes. Subscribe to the transfer.* family only; the legacy payout.* events fire for the same payout. See the event catalog.

If you miss a webhook, poll GET/v1/transfers/{id} with backoff, or reconcile a period with GET/v1/transfers filtered by type=payout.

Retry safely

  • Send a reference with every payout. Resending a create with the same reference returns the original transfer at its current status and doesn't pay out again. Use it to recover from a timeout or a double submit.
  • A reference replays whatever happened the first time, including a failure. To retry a failed payout, send a new reference.
  • If Frame rejects the create with 422, the reference isn't used up. Fix the request and resend it with the same reference.
  • Without a reference, Frame doesn't deduplicate. A retried request can pay out twice.

Handle a rejected or failed payout

A card payout can stop at two points. The POST/v1/transfers reference lists the causes for each.

  • Rejected at creation. The create returns 422 or 404 and Frame sends no funds. Read error_details.message, and error_details.data for field-level detail. Fix the request, or the account, card or merchant setup the message names, and resend with the same reference.
  • Failed after creation. The create returned 201, then the transfer moved to failed and Frame sent transfer.failed. No funds moved. Offer another debit card or an ACH payout, and retry with a new reference.

Frame sets the daily card payout limit, the first-payout cap and the new-card window per merchant. Contact Frame for your values.

Test in sandbox

Use your sandbox key (sk_sandbox_...). In sandbox, card payouts are on for every merchant, with nothing to enable, and a card payout never reaches the card network. Save a test card to the recipient account with POST/v1/payment_methods, then create the payout as in step 3. A sandbox payout settles at once: the transfer moves to succeeded or failed and Frame sends the same webhooks as in live mode.

The test card decides the outcome:

Card numberBrandFundinginstant_availablePayout outcome
4000056655665556VisadebittrueSucceeds
5200828282828210MastercarddebittrueSucceeds
4000056655660011VisadebittrueFails: insufficient funds
4000056655660029VisadebittrueFails: network decline
5105105105105100Mastercardprepaidfalse (card_prepaid_not_eligible)Rejected at creation: Payment method must be a debit card or ACH
4242424242424242Visacreditfalse (card_not_debit)Rejected at creation: Payment method must be a debit card or ACH

Use any future expiry date and any three-digit CVC. Every other sandbox test card is a credit card, like 4242424242424242. The instant_available values assume the account holder's KYC is verified.

Save sandbox cards again

A sandbox card saved before Frame added card funding to its test cards has no funding type, so it reports instant_available: false. Save the card again to get its funding.

Caveats

Use v1 transfers for card payouts

Push-to-card payouts run on POST /v1/transfers. POST /v2/transfers doesn't support card destinations for payouts, and the older POST /v1/payouts is deprecated.

  • No transaction-type field. A payout request takes no network transaction-type indicator, such as Account Funding Transaction (AFT) or Original Credit Transaction (OCT) flags. Frame and its processor set how the push is submitted to the network. If your program needs a specific transaction type, confirm it with Frame before you go live.
  • Rejected creates can leave a transfer. A card payout rejected with 422 by the card, capability or account checks can still leave a transfer in pending with payout: null and reference: null. It moves no funds and shows in GET/v1/transfers. Match transfers to your payouts by reference, and treat a pending transfer with payout: null as rejected.
  • Failure detail. failure_reason, failure_code and failure_message on the transfer are often null for a failed card payout. Use transfer.failed and the steps above to decide what to offer next.
  • No automatic fallback. When a card payout fails, Frame doesn't retry it over ACH. Create a new ACH payout if you want one.
  • One payout per request. There's no bulk endpoint. Create one transfer per recipient.
Frame Assistant

Ask anything about Frame's APIs and products