Moving money with Treasury using OutboundPayment objects
Learn how to create outbound payments to move money out of Treasury financial accounts to third parties.
OutboundPayment
objects represent push-based transfers from your Treasury financial account to a third-party external account using ACH or wire transfer, or another financial account associated with the same platform instantly using the stripe
network. For example, if you want to send money from your financial account to your vendor’s external US bank account, you create an OutboundPayment
to move the funds. The receiving accounts for an OutboundPayment
are either an external bank account or another financial account.
The typical transfer time for outbound payments can range from minutes (when using the Stripe network), same day, to 1-2 business days (when using the ACH network). For more information, see the Money movement timelines guide.
Create an OutboundPayment
Use POST /v1/treasury/outbound_
to create an OutboundPayment
. Among the request’s possible parameters, the following are required:
amount
: Amount in cents to pay.currency
: Three-letter ISO currency code (onlyusd
supported).financial_
: The source financial account funds are sent from.account destination_
orpayment_ method destination_
: Information about the destination of funds for the payment.payment_ method_ data - With
destination_
, you must first set up thepayment_ method PaymentMethod
for outbound flows using a SetupIntent. You must also specify the customer ID that matches theCustomer
object thePaymentMethod
is attached to. Alternatively, you can use an existing legacy BankAccount attached to theCustomer
in place of aPaymentMethod
. - With
destination_
, you can specify payment method details inline. You can use this parameter to specify bank account details or when you’re sending funds to another financial account over the Stripe network.payment_ method_ data
- With
Creating an OutboundPayment to an external bank account
Use POST /v1/treasury/outbound_
to create an OutboundPayment
from the financial account identified by the ID in the financial_
parameter value of the body. The following request adds statement_
and destination_
information.
If successful, the response returns the newly created OutboundPayment
.
Same-day ACH
Beta
Same-day ACH is currently in beta with limited availability, subject to Stripe review and approval. To request access, email [email protected].
If you don’t have access, API calls that include same-day ACH features or parameters return an error.
Using same-day ACH enables sending funds that arrive the same business day if the OutboundPayment
call successfully completes before the cutoff time. To use same-day ACH, set the destination_
parameter to ach
and the destination_
parameter to same_
.
Wire transfer: routing numbers
Some banks might use a separate wire transfer routing number that differs from ACH. Consequently, you might receive an error during wire creation if the routing number on the payment method doesn’t support wire transfers. If you receive this error, you need to add a new payment method with your bank’s wire routing number.
Wire transfer: recipient address
Wire transfers require ACH metadata plus recipient name and billing address. The address is the address of the account holder receiving the wire, not the address of their bank.
When entering the billing_
for a payment method, all address fields must be complete. Attempting to send a wire with incomplete fields on the billing_
results in an error.
Note
When sending a wire using an OutboundTransfer
, if you don’t fill out any address fields, Stripe defaults to the legal entity of the primary Stripe account holder.
Creating an OutboundPayment to a financial account
To move money between financial accounts, call POST /v1/treasury/outbound_
on the origin account and specify the destination account in the destination_
parameter. Both financial accounts must be associated with the same platform, but can be attached to either a connected account or the platform.
The body of your request must be x-www-form-urlencoded
, but the following JSON defines the data you can send.
Retrieve an OutboundPayment
Use GET /v1/treasury/outbound_
to retrieve details for the OutboundPayment
with the associated ID.
If successful, the response returns the OutboundPayment
object with the associated ID. Some of the parameters in the response have additional details that are only returned when you add them as values to the expand[]
parameter. The fields that you can expand have an “Expandable” comment in the following response example. See Expanding Responses to learn more about expanding object responses.
Cancel an OutboundPayment
Use POST /v1/treasury/outbound_
to cancel the OutboundPayment
with the associated ID. The OutboundPayment
object includes a cancelable
parameter with a Boolean value to indicate whether you can cancel the transfer. After an OutboundPayment
submits to the network, the cancelable
value becomes false
and you receive an error from this endpoint for that transfer.
If successful, the response returns the OutboundPayment
object with the status
value set to canceled
.
{ "id": "{{OUTBOUND_PAYMENT_ID}}", "object": "outbound_payment", "livemode": false, "created": 123456, "financial_account": "{{FINANCIAL_ACCOUNT_ID}}", "amount": 1000, "currency": "usd", ... "status": "canceled",
List OutboundPayments
Use GET /v1/treasury/outbound_
to list the OutboundPayments
from the financial account with the associated ID. You can filter the list with the standard list parameters or by status
or by customer
.
{ // Standard list parameters "limit", "starting_after", "ending_before", // Filter by status "status": "processing" | "canceled" | "failed" | "posted" | "returned", // Filter by FinancialAccount (Required) "financial_account": "{{FINANCIAL_ACCOUNT_ID}}", // Filter by Customer "customer": "{{CUSTOMER_ID}}", }
The following request retrieves the last five OutboundPayment objects for the financial account attached to the platform and paid to the identified Customer
.
OutboundPayment states
The following table describes each status and what the possible transition states are.
STATUS | DESCRIPTION | CAN TRANSITION TO STATE |
---|---|---|
processing | The OutboundPayment starting state. Funds are allotted to a pending transaction (but are still part of the current balance). The user can cancel the OutboundPayment while the value of the cancelable parameter is true . | posted , canceled , failed |
failed (terminal) | OutboundPayment failed to confirm. Stripe voids the pending transaction and returns the funds to the user. | N/A |
canceled (terminal) | A user canceled the OutboundPayment before posting. Stripe voids the pending transaction and returns the funds to the user. | N/A |
posted | The OutboundPayment posted and funds have left the account. The underlying transaction posts. | returned |
returned (terminal) | OutboundPayment failed to successfully arrive at the destination. Funds return to the user with a transaction (returned_ ). | N/A |
Testing OutboundPayments
To test your integration end-to-end, we recommend using the SetupIntent requests in test mode to create a PaymentMethod
, then passing that PaymentMethod
into an OutboundPayment
creation request using the destination_
parameter.
Stripe also allows test PaymentMethod
tokens and numbers to trigger specific functionality:
- By passing in a test
PaymentMethod
token todestination_
(forpayment_ method ach
andus_
networks)domestic_ wire - If you’re passing in a test
PaymentMethod
token directly intodestination_
, you must still pass in a customer ID to thepayment_ method customer
parameter. For convenience, Stripe allows you to pass in any existing test mode customer. This differs from live mode, which requires the existingPaymentMethod
to be attached to aCustomer
and that same customer ID passed into thecustomer
parameter.
- If you’re passing in a test
- By passing in test routing and account numbers to
destination_
(forpayment_ method_ data[us_ bank_ account] ach
andus_
networks).domestic_ wire - By passing in the ID of an existing test mode financial account owned by an intra-platform account to
destination_
(for Stripe network).payment_ method_ data[financial_ account]
In all cases, the OutboundPayment
response returns the processing status. Stripe triggers webhooks for the relevant state transitions, and fetching the OutboundPayment
after creation returns the expected state.
CREATES | DESTINATION_PAYMENT_METHOD (WITH ANY EXISTING TEST MODE CUSTOMER) | DESTINATION_PAYMENT_METHOD_DATA[US_BANK_ACCOUNT] |
---|---|---|
OutboundPayment in initial processing state | pm_ |
|
OutboundPayment that transitions to posted (from processing ) | pm_ |
|
OutboundPayment that transitions to posted (from processing ), additionally adding one day to the original expected_ | pm_ |
|
OutboundPayment that transitions to canceled (from processing ) | pm_ |
|
OutboundPayment that transitions to failed (from processing ) | pm_ |
|
OutboundPayment that transitions to returned due to account closure (from processing after posted) | pm_ |
|
OutboundPayment that transitions to returned due to no account (from processing after posted) | pm_ |
|
OutboundPayment that transitions to returned due to invalid account number (from processing after posted) | pm_ |
|
OutboundPayment test helper endpoints
Stripe provides endpoints to help you test OutboundPayments
in different states. Use the test endpoints to move an OutboundPayment
you create directly to a new state of posted
, failed
, or returned
.
Use the test post endpoint to move the identified
OutboundPayment
fromprocessing
toposted
.POST /v1/test_
helpers/treasury/outbound_ payments/{{OUTBOUND_ PAYMENT_ ID}}/post Use the test fail endpoint to move the identified
OutboundPayment
fromprocessing
tofailed
.POST /v1/test_
helpers/treasury/outbound_ payments/{{OUTBOUND_ PAYMENT_ ID}}/fail Use the test return endpoint to move the identified
OutboundPayment
fromprocessing
toreturned
.POST /v1/test_
helpers/treasury/outbound_ payments/{{OUTBOUND_ PAYMENT_ ID}}/return
These endpoints are particularly useful when testing error scenarios, such as returns, which would otherwise require outside action.
For the return
endpoint, include the optional returned_
parameter in the body to indicate why the transfer was returned. If not provided, the transfer defaults to the declined
return code.
{ "returned_details": { "code": "account_closed" | "account_frozen" | "bank_account_restricted" | "bank_ownership_changed" | "could_not_process" | "invalid_account_number" | "incorrect_account_holder_name" | "invalid_currency" | "no_account" | "declined" } }
We also provide a test update endpoint to simulate the posting of tracking details on a test mode Outbound Payment
. The tracking_
field can only be set for test mode objects.
In all cases, Stripe triggers webhooks for each relevant state transition, and fetching the OutboundPayment
after transition returns the expected state.
OutboundPayment webhooks
Stripe emits the following OutboundPayment
events to your webhook endpoint:
treasury.
onoutbound_ payment. created OutboundPayment
creation.treasury.
when anoutbound_ payment. {{new_ status}} OutboundPayment
changes status. Available status value options include:treasury.
outbound_ payment. posted treasury.
outbound_ payment. failed treasury.
outbound_ payment. canceled treasury.
outbound_ payment. returned
treasury.
when theoutbound_ payment. expected_ arrival_ date_ updated expected_
of anarrival_ date OutboundPayment
changes.treasury.
when the tracking details for anoutbound_ payment. tracking_ details_ updated OutboundPayment
are updated.