Manage account obligationsPrivate preview
Track the credit spend, lifecycle, and balances for your connected accounts.
After you activate the connected account’s CreditPolicy, Stripe creates a FundingObligation that allows you to track how much the account owes you throughout a credit period. The beginning of each period automatically creates a new FundingObligation
, triggering an issuing_
event.
The FundingObligation
only tracks cleared Transactions and doesn’t include pending Authorisations. For information on pending Authorisations, refer to List all authorisations.
To demonstrate the process of applying your platform’s funds to a connected account with a CreditPolicy
, let’s say that Barbell (one of Gymbox’s connected accounts) has a 1,000 USD credit limit, and they spend 100 USD on a set of weights. The table below outlines the sequence of events on Stripe:
Action | Barbell issuing balance | Barbell FundingObligation to Gymbox | Gymbox issuing balance |
---|---|---|---|
Barbell starts with a 0 USD balance and no prior spend. Gymbox starts with a 100 USD balance | 0 USD | 0 USD | 100 USD |
Cardholder makes a 100 USD transaction, which is authorised because Gymbox has sufficient funds. Gymbox’s Issuing account drops by 100 USD as a result of an authorisation hold. | -100 USD | 0 USD | 0 USD |
During auth clearing (usually the next day), Stripe:
| 0 USD | 100 USD | 0 USD |
At the end of Barbell’s credit period, Barbell pays off their FundingObligation to Gymbox. | 0 USD | 0 USD | 0 USD |
Track the FundingObligation Lifecycle
Stripe creates a new FundingObligation
on the connected account when the current credit period ends. For example, let’s assume Barbell has a 1 month credit period that ends on the 15th of each month. This means that on the 15th of every month, Stripe:
- Finalizes the
amount_
of the currenttotal FundingObligation
- Creates a new
FundingObligation
for the new period
FundingObligation status
Every FundingObligation
changes status based on the credit period set in the CreditPolicy
and the written credit or collections policy agreed to with Stripe and the bank.
A FundingObligation
can have one of the following statuses:
- Pending: The
FundingObligation
is still accruing spend from the current credit period and theamount_
might change.total - Unpaid: The
amount_
on thetotal FundingObligation
is finalised, as indicated by thefinalized_
timestamp. The account needs to fully pay off this obligation by theat due_
timestamp.at - Paid: The
FundingObligation
is fully paid off by the connected account. - Past due: The
FundingObligation
isn’t fully paid and thedue_
timestamp has passed. Stripe might report past-dueat FundingObligations
to bank partners. - Charged off : The
FundingObligation
has exceeded the number of days until charge off specified in your written credit or collections policy. When setting up your programme, Stripe configures the number of days until charge off after verifying this value against your written credit policy documentation. You can continue to collect repayment from the connected account after theFundingObligation
reaches the charged off state. Stripe might report past-dueFundingObligations
to bank partners. Contact Stripe’s Compliance team by submitting a request through the Change Request form to:- Change the number of days until charge off, as specified in your policy documentation
- Plan to sell your charged-off debt
- Needs refund: The
FundingObligation
has a negativeamount_
where the platform owes the connected account. This can only happen when a connected account receives an amount from refunds or won disputes that exceeds the amount spent throughout the credit period.total
The due_
field on the FundingObligation
is determined by the credit_
field on the FundingObligation
and the days_
field on the CreditPolicy
.
Retrieve FundingObligations
Over time, a connected account will have multiple paid off (or past_
) FundingObligation
instances and at most a single currently pending FundingObligation
. To fetch the FundingObligation
for the current period, run this command:
Example response
[ { "id": "ifo_123", "livemode": true, "created": 1654628149, "amount_total": 10000, "amount_outstanding": 10000, "amount_paid": 0, "currency": "usd", "status": "unpaid", "due_at": 1654714851, "owed_to": "acct_123", "credit_period_starts_at": 1654625149, "credit_period_ends_at":1654713851, "paid_at": nil, "finalized_at": 1654713860, }, ]
To fetch FundingObligations
with a particular status, pass in the specific status value when requesting a list of FundingObligations
. This API call retrieves FundingObligations
with a status
of past_
:
Get Transactions for a FundingObligation
Retrieve the list of transactions that contributed to a connected account’s FundingObligation
by passing the funding_obligation_for_account parameter in the List all transactions API request:
Example response
{ "object": "list", "url": "/v1/issuing/transactions", "has_more": false, "data": [ { "id": "ipi_123", "object": "issuing.transaction", // various other fields "funding_obligation_for_platform": "ifo_123", "funding_obligation_for_account": "ifo_456", // various other fields }, { "id": "ipi_123", "object": "issuing.transaction", // various other fields "funding_obligation_for_platform": "ifo_789", "funding_obligation_for_account": "ifo_456", // various other fields }, {...} ] }
Summary of webhooks
As a reminder, you can monitor these four webhooks:
issuing_
: Triggers whenever acredit_ policy. created CreditPolicy
is created, which happens when the capability is requested for the connected account.issuing_
: Triggers whenever acredit_ policy. updated CreditPolicy
is updated, which can happen when the platform updates the connected account’s policy.issuing_
: Triggers whenever afunding_ obligation. created FundingObligation
is created, which happens around the beginning of each new credit period for the connected account.issuing_
: Triggers whenever afunding_ obligation. updated FundingObligation
is updated, which happens whenever the funding obligation changes status or amount fields, or has been updated to indicate repayment.
Pay off an account’s FundingObligation
While the FundingObligation
shows you how much is owed by a connected account at the end of the credit period, you’re still responsible for collecting repayment from the connected account. Use Stripe’s Payment Intents API to debit the connected account’s external bank account (or accept a card payment via Stripe Checkout or Stripe Invoicing).
Make sure you record any payments received from the connected account in the account’s FundingObligation
to accurately reflect their available credit. Stripe determines whether to approve an authorisation based on an account’s available credit. Stripe might report past-due and charged-off FundingObligations
to bank partners.
Collect repayment
You can choose the best mechanism for collecting repayment from your end user, but we recommend that you use Stripe’s Payments APIs to keep all records within Stripe for better reconciliation of each user’s card spend and repayment. After successfully collecting payment, make sure you update the FundingObligation
to reflect this. This step doesn’t happen automatically when you use Stripe’s Payments APIs. Stripe determines past_
based on the days_
parameter set in the CreditPolicy
. Stripe determines charged_
based on your written credit or collections policy that defines the number of days after a past_
obligation is charged_
.
Record repayments in FundingObligation
After collecting payment from a connected account, update their FundingObligation
to reflect the repayment. FundingObligations
states of unpaid
, past_
, or charged_
impact how many funds are still available for the connected account to spend. For example, let’s assume Barbell has a 1,000 USD credit limit and they buy a treadmill for 900 USD. Barbell’s FundingObligation
has 900 USD as the amount_
owed, and they have 100 USD of available credit left to spend until they start paying off what’s owed.
Let’s say Barbell pays back 500 USD to Gymbox at the end of the credit period. Gymbox must update Barbell’s FundingObligation
to reflect the repayment. This update decreases the amount_
on the FundingObligation
to 400 USD and increases Barbell’s available credit from 100 to 600 USD.
Let’s say Barbell’s credit terms specify a 90-day period post due_
for the amount_
on the FundingObligation
to be charged off. On past_
over 90 days, Barbell’s amount_
of 400 USD is charged off. 30 days after charging off the amount_
on the FundingObligation
, Gymbox receives a 100 USD payment from Barbell. Gymbox needs to record this repayment with the /pay
endpoint to reduce the charged off amount_
on the FundingObligation
. This increases the available credit from 600 to 700 USD. If Gymbox closes Barbell’s credit line and reports the reason prior to collecting in full, the charged off amount_
persists and subsequently recovered amounts can no longer be applied to reduce the amount_
.
Example response
{ "id": "ifo_123", "status": "unpaid", "amount_total": 90000, "amount_outstanding": 40000, "amount_paid": 50000, "owed_to": "acct_123", // other fields }
This API call increases the amount_
and decreases the amount_
on the FundingObligation
. When amount_
on a FundingObligation
, Stripe updates the status of the obligation to paid
, regardless of the previous state being unpaid
, past_
, or charged_
. These updates trigger an issuing_
event.
We recommend that you automate both the process of collecting repayment and the process of updating the FundingObligation
, and combine them into sequential calls. That way, immediately after you successfully collect repayment, you update the connected account’s FundingObligation
to inform Stripe to increase their available credit.
Update the paid amount on the FundingObligation
If you make an erroneous repayment or want to update the connected account’s amount_
for another reason, you can do it with the /pay
endpoint using the amount_
field.
Example response
{ "id": "ifo_789", "status": "unpaid", "amount_total": 90000, "amount_outstanding": 45000, "amount_paid": 45000, "owed_to": "acct_123", // other fields }
This sends an issuing_
event.
Update metadata on FundingObligations
You can update the metadata on a FundingObligation
to associate additional data with a FundingObligation
. For example, you might want to record the OutboundPayment ID that corresponds to a repayment.
Example response
{ "id": "ifo_789", "status": "unpaid", "amount_total": 90000, "metadata": { "repayment_id": "obp_1NUy3y2eZvKYlo2C15gktUET" }, "owed_to": "acct_123", // other fields }
This sends an issuing_
event.
Available credit
Stripe doesn’t currently provide a field that shows the connected account’s available credit balance. You can compute this number through the following formula:
available credit = credit_ - sum(FundingObligation. )1 |
The funds available for a CA to spend at all times is available credit and issuing balance.
1sum(FundingObligation.
) is shorthand for you to sum up the amount_
on all FundingObligations
in the connected account.
Track the flow of funds
While it’s not necessary for giving your connected accounts access to a credit account, you might want to track the state of authorisations, transactions, and transfers created when a connected account spends funds extended from your platform.
Assume the following starting state:
Account | Issuing balance |
---|---|
Gymbox Issuing Account (Platform) | 70 USD |
Barbell Issuing Balance (Connected Account) | 0 USD |
Authorisations
Barbell, who has a CreditPolicy
with a credit_
of $100 setup with Gymbox, spends $10. This results in a $10 authorisation on Barbell’s account:
{ "id": "iauth_1JVXl82eZvKYlo2CPIiWlzrn", "object": "issuing.authorization", "amount": 1000, "currency": "usd", "approved": true, "authorization_method": "online", "balance_transactions": [ { "id": "txn_1234XYZ", "object": "balance_transaction", "amount": -1000, "type": "issuing_authorization_hold", ... } ], "card": {...}, ... }
Simultaneously, this generates a hold on Gymbox’s issuing balance for the same amount, making sure that the funds are reserved and can’t be used for transactions by Gymbox’s other connected accounts:
{ "id": "txn_1Mgr6fXpL7qsPGZtDwrMkq3S", "object": "balance_transaction", "amount": -1000, "available_on": 1677682692, "created": 1677682692, "currency": "usd", "description": "Platform hold for authorization (account: acct_1MgC5JRcH5icH3Nz, authorization: iauth_1Mgr6dRcH5icH3NzezZCHnJF)", "exchange_rate": null, "fee": 0, "fee_details": [], "net": -1000, "reporting_category": "issuing_authorization_hold", "source": { "id": "iph_1Mgr6eXpL7qsPGZtAMb7x0N6", "object": "issuing.platform_hold", "amount": 1000, "currency": "usd", "originating_account": "acct_1MgC5JRcH5icH3Nz", "originating_authorization": "iauth_1Mgr6dRcH5icH3NzezZCHnJF", "originating_balance_transaction": "txn_1Mgr6dRcH5icH3NzYGRkgcn7" }, "status": "available", "type": "issuing_authorization_hold" }
The balance is now:
Account | Issuing balance |
---|---|
Gymbox Issuing Account (Platform) | 60 USD |
Barbell Issuing Balance (Connected Account) | -10 USD |
Transfers & transaction captures
Before Issuing authorisations are captured and Issuing transactions are created, funds are transferred to the connected accounts. Then, holds are released from the platform.
On Barbell’s account, the authorisation has been updated and closed, releasing the held funds:
{ "id": "iauth_1JVXl82eZvKYlo2CPIiWlzrn", "object": "issuing.authorization", "amount": 1000, "currency": "usd", "approved": true, "authorization_method": "online", "balance_transactions": [ { "id": "txn_1234XYZ", "object": "balance_transaction", "amount": -1000, "type": "issuing_authorization_hold", ... }, { "id": "txn_4t355646t54w2", "object": "balance_transaction", "amount": 1000, "type": "issuing_authorization_release", }, ], "card": {...}, "status": "closed", "transactions": [ { "id": "ipi_1032HU2eZvKYlo2CEPtcnUvl", "object": "issuing.transaction", "amount": -1000, "authorization": "iauth_1JVXl82eZvKYlo2CPIiWlzrn", "balance_transaction": "txn_1345r1KCr4trgtrg0WfNdUCbG1w", ... } ... }
The funds held on the platform’s account are also released:
{ "id": "txn_1Mgr6fXpL7qsPGZtDwrPz7bA", "object": "balance_transaction", "amount": 1000, "available_on": 1677682692, "created": 1677682692, "currency": "usd", "description": "Released platform hold for authorization (account: acct_1MgC5JRcH5icH3Nz, authorization: iauth_1Mgr6dRcH5icH3NzezZCHnJF)", "exchange_rate": null, "fee": 0, "fee_details": [], "net": 1000, "reporting_category": "issuing_authorization_hold", "source": { "id": "iph_1Mgr6eXpL7qsPGZtAMb7m8Z3", "object": "issuing.platform_hold", "amount": -1000, "currency": "usd", "originating_account": "acct_1MgC5JRcH5icH3Nz", "originating_authorization": "iauth_1Mgr6dRcH5icH3NzezZCHnJF", "originating_balance_transaction": "txn_1Mgr6dRcH5icH3NzYGR7bA4c" }, "status": "available", "type": "issuing_authorization_hold" }
Stripe transfers funds from Gymbox to Barbell’s Issuing account and a Transfer object is created:
{ "id": "tr_3JeQsp2eZvKYlo2C13DagtB0", "object": "transfer", "amount": 1000, "amount_reversed": 0, "balance_transaction": "txn_1032HU2eZvKYlo2CEPtcnUvl", "created": 1646912059, "currency": "usd", "description": null, "destination": "acct_1032D82eZvKYlo2C", "livemode": true, "metadata": {}, "reversals": { "object": "list", "data": [], "has_more": false, "url": "/v1/transfers/tr_3JeQsp2eZvKYlo2C13DagtB0/reversals" }, "reversed": false, "source_transaction": null, "issuing_transaction": "ipi_1032HU2eZvKYlo2CEPtcnUvl", "metadata": {}, "source_balance": { "type": "issuing", }, // New destination_balance returned field links to the BT on the connected account side "destination_balance": { "type": "issuing", "issuing": { "balance_transaction": "txn_123", }, }, // ... other fields ... }
This sequence of hold, release and transfer happens before Stripe creates the associated Issuing transaction. The balance is now:
The balance is now:
Account | Issuing balance |
---|---|
Gymbox Issuing Account (Platform) | 60 USD |
Barbell Issuing Balance (Connected Account) | 0 USD |
If there is no original authorisation for a transaction and the connected account doesn’t have sufficient funds to cover it, the platform balance transfer and transaction creation flow still occurs.
Refunds
When a connected account receives a refund, Stripe attempts to determine whether it pertains to a transaction that’s funded from that connected account’s balance or platform’s account. If Stripe determines that it’s funded from the connected account’s balance, the funds return to the connected account’s balance. If the transaction is funded from the platform’s account (for example, from the connected account’s credit line), the connected account’s FundingObligation
reduces by that amount. The refund is then transferred to the platform’s account because the platform already paid Stripe for the transaction on behalf of the connected account.
Let’s say Barbell has a 100 USD FundingObligation
. If the amount is refunded, then:
- Barbell’s
FundingObligation
decreases from 100 to 0 USD - The 100 USD is transferred to Gymbox’s Issuing account, increasing their platform balance by 100 USD
It’s possible for the amount_
on a FundingObligation
to be negative, which happens if the only transaction in a credit period is a refund and there’s no other card spend. The FundingObligation
indicates a status of “Needs refund.” A negative FundingObligation
can occur if there’s card spend but the amount of total refunds is higher than the total card spend.
Disputes
When a customer disputes a credit spend transaction on a connected account, the dispute follows the process outlined in Issuing disputes. Lost disputes result in no action (no credits are issued to the connected account). Won disputes issue a credit to the connected account in the same manner as described in the Refunds section.
Authorisation declines
Authorisations made on a connected account with a CreditPolicy
could be declined for the following reasons:
- The connected account attempts to spend beyond its available credit limit. In this instance, the connected account might have unpaid
FundingObligations
that have reduced its available credit. Make sure that your connected account’sFundingObligations
are always up-to-date to preventAuthorization
declines. - Your available platform Issuing account reaches zero, and the available Issuing balance of your connected account is also at zero. Top-up your Issuing account with sufficient funds to avoid declines.