End-to-end walkthrough
This walkthrough composes four flows into one merchant integration: onboard a customer-account, verify their identity, accept a payment from them, and pay them out. It's the realistic shape of a Frame integration — not the five-minute quickstart, but the actual workflow most platforms build.
Each stage links to the dedicated guide for that primitive. This page is the connective tissue: how the pieces fit together, what state each stage leaves the account in, where the natural seams are if you only need part of the flow.
If you haven't run the quickstart yet, do that first. It's four curl commands that prove your sandbox is working before you take on a composed flow.
What you're building
By the end of this walkthrough you'll have:
- A verified customer-account on Frame, with identity (
kyc) confirmed and payment capabilities (card_sendfor charging,card_receiveorbank_account_receivefor paying out) active. - A successful card charge against that account.
- A successful payout to that account.
Two real money-movement flows on a real verified account. From there, every Frame integration is variation on these two shapes.
Prerequisites
| Requirement | Details |
|---|---|
| Frame API key | Secret key for server-to-server calls. See Install. |
| frame-js (client-side) | Required for production card collection; sandbox can use raw card numbers. The walkthrough uses sandbox for brevity. |
| Customer profile data | At minimum: name, email, phone. KYC will collect the rest through the hosted flow. |
| Return URL | Where Frame redirects the account holder after onboarding completes. |
Set your sandbox key:
export FRAME_SECRET_KEY=sk_sandbox_...
Stage 1 — Create the customer-account
The Account is the durable record of your customer on Frame. You'll request the capabilities the relationship needs up front; Frame stages the activation based on what data lands when.
For a customer you'll both charge and pay out to, request three capabilities together:
curl --request POST \
--url https://api.framepayments.com/v1/accounts \
--header "Authorization: Bearer $FRAME_SECRET_KEY" \
--header 'Content-Type: application/json' \
--data '{
"type": "individual",
"capabilities": ["kyc", "card_send", "card_receive"],
"external_id": "user_42",
"profile": {
"individual": {
"email": "marcia@example.com",
"name": { "first_name": "Marcia", "last_name": "Longo" },
"phone": { "number": "3107484186", "country_code": "1" }
}
}
}'
The response returns the Account with four capabilities in pending — the three you requested plus phone_verification, which Frame auto-adds because kyc depends on it. KYC needs identity verification; card_send needs a card; card_receive needs both KYC + a debit card. Stage 2 closes most of those gaps in one move.
Deep dive: Accounts concept for the model; Run onboarding for the canonical create flow with all the variations.
Stage 2 — Onboard the customer (KYC + payment method + payout election)
Hand the account holder off to Frame's hosted onboarding session. The session is composed of steps derived from the capabilities you requested in Stage 1. For our kyc + card_send + card_receive shape, the account holder sees these labels in the hosted UI:
- "Get started" (the
phone_verificationstep — OTP to confirm phone ownership). - "Basic info" + "Personal info" (the KYC identity collection — name, address, DOB, last 4 of SSN; selfie + document capture if the verification requires them).
- "Payment Method" (the
payment_methodstep — collects a card and attaches it to the account). - "Payouts" (the
payoutsstep — elects which payment method acts as the payout destination. With a debit card collected in the previous step, the same card is offered as the payout destination here; the account holder confirms and Frame setsaccount.payout_payment_method_id).
One Frame-hosted flow, Turbo-driven page transitions between steps. Bank-account connections (when bank_account_send or bank_account_receive is requested) hand off to Plaid mid-flow — that's the only third-party redirect to expect. Create the session:
curl --request POST \
--url https://api.framepayments.com/v1/onboarding_sessions \
--header "Authorization: Bearer $FRAME_SECRET_KEY" \
--header 'Content-Type: application/json' \
--data '{
"account_id": "<account_id>",
"return_url": "https://yourapp.com/onboarding/complete"
}'
The response includes a url. Redirect the account holder there from your application:
window.location.href = session.url;
Frame walks them through all the capability-derived steps. When they finish, Frame redirects them to your return_url.
The redirect is not a success signal. It fires when the account holder clicks through, not when the verifications resolve. Subscribe to capability.activated and capability.disabled webhooks (or poll the account) to know when the capabilities actually settle.
By the end of Stage 2 — assuming verification passes and a debit card was attached — kyc is active, card_send is active, card_receive is active, and account.payout_payment_method_id (returned in the account serializer) points at the card the account holder elected as their payout destination.
If the attached card is a credit card, card_receive transitions to ineligible rather than active — the payout-method election scope filters for debit cards only, so the payouts step has no eligible card to offer and stays pending. There's no automatic re-attach prompt; you'll need to surface a remediation path in your application (request the account holder attach a debit card via a fresh onboarding session, or fall back to an ACH payout method).
Deep dive: Onboarding concept for the session model; Run onboarding for the full step-by-step; Run KYC for re-verification and the existing-account branch.
Stage 3 — Accept a payment from the customer
The customer is verified and has a card attached. Create a transfer to charge them:
curl --request POST \
--url https://api.framepayments.com/v1/transfers \
--header "Authorization: Bearer $FRAME_SECRET_KEY" \
--header 'Content-Type: application/json' \
--data '{
"amount": 5000,
"currency": "USD",
"account_id": "<account_id>",
"source_payment_method_id": "<payment_method_id>",
"description": "Order #1042"
}'
Charge transfers settle synchronously — the response returns the Transfer with "status": "succeeded" if the processor accepts. No webhook required for the happy path; webhooks matter for failure modes and asynchronous flows (3DS challenges, ACH timing).
The account_id is the attribution target — the customer-account whose record the transfer hangs off of. The source_payment_method_id is the card being charged. See Transfers concept for how the flow shape is selected by which payment-method fields you set.
Deep dive: Accept a payment for the production-shape flow with frame-js card collection, 3DS handling, idempotency, and webhook patterns.
Stage 4 — Pay the customer out
The customer earned money on your platform. Now you want to pay them. Same Transfer primitive, opposite direction: no source_payment_method_id, set destination_payment_method_id instead — pointing at the payout method elected during Stage 2 (account.payout_payment_method_id).
For a customer whose Stage 2 attached a debit card and elected it for payouts, that's the same payment_method_id you used in Stage 3 — but the API surfaces it as a separate field because the election is what makes it valid as a destination, not just being on the account.
curl --request POST \
--url https://api.framepayments.com/v1/transfers \
--header "Authorization: Bearer $FRAME_SECRET_KEY" \
--header 'Content-Type: application/json' \
--data '{
"amount": 4500,
"currency": "USD",
"account_id": "<account_id>",
"destination_payment_method_id": "<payment_method_id>",
"description": "Payout — Order #1042"
}'
Payouts settle asynchronously. The response comes back pending; the processor confirms over the network, not in-line. Listen for transfer.succeeded (funds posted) or transfer.failed (rejected). Push-to-card typically settles in minutes; ACH in 1-2 business days.
For ACH payouts (bank_account_receive), include speed: standard (1-2 business days), same_day (same business day), or asap (fastest available rail).
Deep dive: Pay out to an account for the full flow, payout-category billing agreement requirements (a real production trap), failure modes, and the same-day-vs-standard tradeoff.
What you have now
You've taken one customer end-to-end: from cold signup, through identity verification, to a successful charge and a successful payout — all anchored to a single Account record. Every future Frame integration is a variation on this shape:
- Marketplace — Stage 3 collects from buyer accounts; Stage 4 pays out to seller accounts. Two accounts, both verified, money flows through your platform.
- Creator platform — Stage 1-2 onboards creators with
creator_shieldfor continuous risk monitoring; Stage 4 is the primary loop (payouts on a schedule). - Subscription business — Stage 3 runs on a cycle, anchored to the same verified account.
- Vertical with regulatory checks — add
age_verificationorgeo_compliancein Stage 1'scapabilitiesarray; Frame stages the verifications inside the same onboarding session.
The four primitives (Account, Capability, OnboardingSession, Transfer) compose into every flow Frame supports. The walkthrough is the smallest set that exercises all four.
Common shapes
Skip identity verification for buyer-only accounts. If your platform only charges customers and never pays them out, you don't need kyc. Request card_send alone; Stage 2 collapses to just card attachment with no document capture. See Accept a payment for the buyer-only shape.
Add capabilities later. A customer who only paid you at first can later opt into payouts. Add card_receive via POST /v1/accounts/:id/capabilities and create a fresh onboarding session — Frame scopes the new session to only the additional requirements, not what's already on file.
Re-verify an existing customer. If a customer's information changes (name change, new address), update the profile fields. Frame re-evaluates kyc automatically. See Run KYC for the re-verification path.
Next steps
- Transfers concept — the lifecycle and state model behind every charge and payout
- Accounts concept — Customer-vs-Account distinction, types, state rollup
- Capabilities concept — the 13 capability types and their dependencies
- Accept a payment — production-shape charge integration
- Pay out to an account — production-shape payout integration with the billing-agreement warning