# Connect

Connect's v2 accounts: act as a connected account, onboard a seller whose capabilities activate on the clock, pay it with destination charges and bill an account as a customer.

Connect lets your platform create v2 accounts and act as them, so you can test onboarding, destination charges and payouts to sellers without a real connected account.

### Accounts

`/v2/core/accounts` creates, reads, updates, lists and closes accounts. Each write sends its v2 events: `v2.core.account.created`, `.updated` or `.closed`; on a create, a part event such as `v2.core.account[identity].updated` for each of identity, defaults, merchant, recipient and customer it sets, and on an update the merchant part's; and a `capability_status_updated` event for each capability status the write moves. `GET /v2/core/events` lists them, and a thin event destination whose `enabled_events` names them receives them. Their `related_object.url` names the part with `?include=`. No webhook endpoint receives them, as they have no v1 form.

`include=requirements` answers `{entries, summary}`: an onboarded seller owes `external_account` until it holds a bank account and the identity checks until its activation, then reads `entries: []` and `summary: null`, while `future_requirements` reads `null`. A stored identity keeps only each ID number's type (`us_ssn`, `us_ssn_last_4`), never its value, in reads and in `v2.core.account[identity].updated`, and its address countries read in upper case.

`POST /v1/accounts/{account}/external_accounts` creates a bank account for a v2 account; it is create only, as `GET /v1/accounts/{account}/external_accounts` is not served and answers "Unrecognized request URL". `GET` and `POST /v1/balance_settings` hold one setting per account: a connected account reads `payments.payouts.status` `enabled` and `payments.debit_negative_balances` `false` once its `stripe_balance.payouts` capability is active, and `disabled` and `true` before.

### Acting as an account

A request whose `Stripe-Account` or `Stripe-Context` header names an account your key created acts as that account: its PaymentIntents, charges, refunds, balance, balance transactions, balance settings, payouts and events are its own. Acting as an account needs a merchant configuration, or a recipient one requesting `stripe_transfers`, before any parameter is checked. A header naming an account your key closed, or one it never created, answers 403 `account_invalid` (`api_error`), and the request creates nothing.

An event a request causes while acting as an account is that account's: its v1 form carries `account` and `context` naming it, and its v2 form carries `context`. `GET /v1/events` and `GET /v2/core/events` list and retrieve it only when read as that account. So is every event a pending refund's settlement or a subscription's clock sends for an object made acting as the account. A webhook endpoint created with `connect=true` takes only connected accounts' events, and renders `application` as a stand-in `ca_` id where Stripe shows your platform's own Connect application; every other endpoint takes only the platform's events. `test_helpers.test_clock.ready` and `checkout.session.expired` stay the platform's even for a test clock or Checkout Session made acting as an account (inferred).

### Onboarding and capabilities

A seller the create onboards whole, with an individual identity carrying date of birth, address, ID number and terms-of-service acceptance, and a `defaults.profile.business_url`, reads `card_payments` `pending` until 85 seconds after its `created`, then `active`, with or without a bank. Once it holds an external account, its `payouts` read `pending` from 18 seconds and `active` from 85; without one they stay `restricted`. Each status that moves sends its `capability_status_updated` event, naming no request, as Stripe's activation does. Until activation, a charge as the seller answers "Your account cannot currently make charges." Move past it with `POST /_rystic/clock {"advance_seconds": 85}`, or run with `RYSTIC_PACE=1` so the clock follows real time.

A `card_payments` capability re-requested after Stripe unfreezes it reads `active` at once on a seller past its activation; on one frozen before it, `card_payments` reads `pending` again, and `payouts` too while it holds a bank, until the seller's own activation time.

### Destination charges

A PaymentIntent with `transfer_data[destination]`, paid at a create with `confirm=true`, a confirm or a capture (`POST /v1/charges/{charge}/capture` of its charge included), is a destination charge. It credits the platform the charge's net as any card payment does, then transfers a share to the connected account: `transfer_data[amount]` when set, capped at the captured amount, or else the whole amount. The account gains the share less any `application_fee_amount`, so with no application fee on a whole-amount transfer the platform loses the card fee. `transfer_data[amount]` beside `application_fee_amount` answers 400 `parameters_exclusive`, as Stripe does. `POST /v1/charges` takes no Connect parameters.

The charge carries `destination`, `transfer_group` (`group_` plus the PaymentIntent's id), `transfer` (a `tr_` id) and, with an application fee, `application_fee` (a `fee_` id) at once, where Stripe names the last two about 6 seconds later; no route reads the transfer or the fee. The platform lists a `transfer` balance transaction for minus the share, its source the transfer that `transfer.created` announces, and with a fee an `application_fee` one for plus the fee, beside the `charge` row. The account lists a `payment` balance transaction whose amount is the share, its fee the application fee with one `application_fee` line in `fee_details` and its net the credit; its source is the `py_` payment, which the account reads with `GET /v1/charges/{id}` and lists with `GET /v1/charges`. The `py_` payment sends `payment.created` as the account's, with `request` `{id: null, idempotency_key: null}`: it lists among the account's events and reaches the platform's Connect endpoints, never the platform's own list. The simulator moves both balances and lists the event at the charge, where Stripe shows them seconds later.

### Refunding a destination charge

Every refund create route takes `reverse_transfer` and `refund_application_fee` on a destination charge. `reverse_transfer=true` takes the refund's proportion of the share back from the account: the refund's `transfer_reversal` names a `trr_` reversal. The platform lists a `transfer_refund` row, and the account a `payment_refund` row and a `pyr_` refund of its `py_` payment, whose `amount_refunded` grows. `refund_application_fee=true` also returns the refund's proportion of the fee, as an `application_fee_refund` row on the platform and an `adjustment` row on the account. Each refund's shares round half up on their own, and the refund that leaves the transfer fully reversed returns the rest of the fee, as Stripe's do.

Stripe refuses a refund whose reversal rounds to 0 ("You cannot reverse a transfer for amount=0.") and a fee refund without `reverse_transfer`; the simulator accepts both. Without `refund_application_fee` the account keeps paying the fee, so its balance can go negative, as Stripe's does. Without `reverse_transfer` the platform alone bears the refund. Stripe's `reserve_transaction` rows on such a refund are not modeled.

### Payouts to a seller

A connected account's payout names as `destination` the bank account its external account create returned, and draws on that account's own balance ([Balance and payouts](https://www.rystic.ai/docs/simulators/stripe/features/balance.md)).

### Accounts as customers

An account with the customer configuration pays as `customer_account` on a PaymentIntent create, update or list, a SetupIntent update and a PaymentMethod attach, which answer it beside the `cus_` id of its v1 customer. `GET /v1/customers/{account id}`, or that `cus_` id, answers that customer, and a PaymentIntent created for the `cus_` id carries `customer_account`. An unknown or closed account answers 400 `resource_missing` wherever `customer_account` is taken (404 on a subscription create). On those five PaymentIntent, SetupIntent and PaymentMethod operations, an account without the customer configuration answers 400, and on the four writes, `customer` beside `customer_account` answers 400.

---

Source: https://www.rystic.ai/docs/simulators/stripe/features/connect · Markdown: https://www.rystic.ai/docs/simulators/stripe/features/connect.md
