API & Agent Onboarding
Use this flow when your integration provisions a merchant before the developer visits the Flint dashboard. Your backend can create the Flint account, issue the first sandbox key, and submit the merchant's onboarding intent. Identity, business verification, documents, payout details, provider authentication, and provider agreement acceptance stay in an embedded browser component owned by the account owner.
If you can use the dashboard, create a test API key there and start with Accept Your First Payment. This guide is for integrations that own merchant provisioning.
The flow is:
POST /v1/onboarding/startPOST /v1/onboarding/verify-emailGET /v1/onboarding/state- Issue the initial sandbox key as soon as
can_issue_api_keyis true POST /v1/onboarding/advancewith profile, country, and requested capabilities- Create an embedded session when
next_step.launchrequires a human - Poll onboarding state after the browser exits and while review is pending
Key issuance and provider verification are independent actions. The initial key becomes available after email verification and sandbox provisioning. It does not bypass capability gates: payments and payouts remain unavailable until their requested capabilities are ready.
Before you start#
- Use
https://api.withflintpay.comfor both test and live requests. - Sandbox isolation comes from sandbox-bound
flint_test_...keys. - Keep the temporary
verification_tokenonly until email verification finishes. - Keep
onboarding_session_tokenon your backend. It is setup authority, not a normal resource credential. - Be prepared to serve an authenticated browser page to the merchant's owner.
An agent cannot finish the email step unless it can read the mailbox or hand the code to a human. The verification token expires after 15 minutes.
1. Start and verify email#
curl -X POST https://api.withflintpay.com/v1/onboarding/start \
-H "Content-Type: application/json" \
-H "Idempotency-Key: onboarding-start-owner-example" \
-d '{
"email": "owner@example.com",
"first_name": "Jane",
"last_name": "Doe"
}'
Save data.verification_token, read the short-lived code from the email, then verify it:
curl -X POST https://api.withflintpay.com/v1/onboarding/verify-email \
-H "Content-Type: application/json" \
-H "Idempotency-Key: onboarding-verify-owner-example" \
-d '{
"verification_token": "devver_v1.example",
"verification_code": "482193"
}'
Save data.onboarding_session_token and data.default_sandbox_id. Flint has created the user, merchant, direct owner membership, and default private sandbox. Read state next because the verification response is not the authoritative list of currently available actions.
2. Read the state machine#
curl https://api.withflintpay.com/v1/onboarding/state \
-H "Authorization: Bearer $ONBOARDING_SESSION_TOKEN"
Drive the integration from these fields:
| Field | Meaning |
|---|---|
status | Overall workflow class: needs_input, needs_external_action, waiting_for_review, ready_for_api_key, or complete. |
next_step | The single primary onboarding action. |
pending_steps | Ordered onboarding work after the primary action. |
available_actions | Safe actions that can run concurrently with next_step, such as initial key issuance. |
can_issue_api_key | Whether the one-time onboarding key operation is currently allowed. |
requirements | Normalized Flint requirement IDs. |
A state can be needs_external_action and also have can_issue_api_key: true. Do not wait for provider verification to issue the initial key.
3. Issue the initial sandbox key#
When can_issue_api_key is true:
curl -X POST https://api.withflintpay.com/v1/onboarding/api-key \
-H "Authorization: Bearer $ONBOARDING_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: onboarding-first-key-owner-example" \
-d '{"name":"First backend integration"}'
If you omit sandbox_id, Flint binds the key to the default sandbox. The starter scopes include onboarding read, onboarding write, and merchant-account-session write authority so the key can continue this workflow. Store secret_key immediately because it is shown once.
Use this key for ordinary sandbox API development, but expect capability-dependent calls to fail until onboarding makes the corresponding capability ready. For example, a merchant that did not request card acceptance receives CAPABILITY_NOT_REQUESTED from card-payment operations.
4. Submit account intent#
When next_step.machine_completable is true, call the method and endpoint it returns. Initial account provisioning uses POST /v1/onboarding/advance:
curl -X POST https://api.withflintpay.com/v1/onboarding/advance \
-H "Authorization: Bearer $ONBOARDING_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: onboarding-advance-owner-example" \
-d '{
"profile": {
"support_email": "help@your-business.example",
"support_phone": "+12125550123",
"website_url": "https://your-business.example"
},
"country": "US",
"requested_capabilities": [
"accept_card_payments",
"receive_payouts"
],
"sandbox_id": "test_01JQEXAMPLEDEFAULT12345678"
}'
Use a real, publicly reachable business website. Reserved example domains above illustrate the JSON shape but do not pass provider website review.
requested_capabilities is an exact Flint capability set:
- Omit it for the baseline
accept_card_paymentsplusreceive_payoutsset. - The baseline pair is the only supported initial set. Flint returns
CAPABILITY_SET_UNSUPPORTEDforreceive_payoutsalone before creating an account. accept_card_paymentsrequiresreceive_payouts; Flint returnsCAPABILITY_DEPENDENCY_REQUIREDinstead of silently adding it.- Unknown and provider-shaped values are rejected.
Country and capability intent become immutable when provisioning starts. Retrying the same request is idempotent. A different country or capability set returns an explicit immutable-intent error and never creates a replacement account.
Do not send legal-entity artifacts, account tokens, identity fields, documents, bank details, or agreement attestations to advance. Those fields belong in embedded Account Onboarding and unknown request fields are rejected.
5. Hand the browser step to the owner#
When state returns next_step.launch, copy that launch literally. It names the component, recommended policy, endpoint, and sandbox. Create the browser credentials only from your backend:
curl -X POST https://api.withflintpay.com/v1/merchant-account-sessions \
-H "Authorization: Bearer $FLINT_TEST_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: account-session-owner-example" \
-d '{
"components": ["account_onboarding"],
"collection_strategy": "upfront",
"future_requirements": "include",
"sandbox_id": "test_01JQEXAMPLEDEFAULT12345678"
}'
Authenticate the human in your product and verify that they are allowed to manage this merchant before returning the session response to their browser. A secret Flint key is machine authority to mint sensitive browser access. Do not expose the key itself, accept a merchant ID as authorization, or put the client secret or launch token in a link.
Mount the component with the exact component_props in the response. Connect.js can request a new client secret while the component remains mounted. Your backend sends the latest rotated token to the dedicated endpoint:
curl -X POST https://api.withflintpay.com/v1/merchant-account-sessions/refresh \
-H "Authorization: Bearer $FLINT_TEST_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: account-session-refresh-owner-example" \
-d '{"launch_token":"aslaunch_v1.example"}'
See Merchant Account Sessions for executable vanilla and React recipes, token rotation, multi-component sessions, CSP, authentication popups, headless secure-link design, and sandbox verification values.
Partner-install tokens cannot create or refresh account sessions. The partner OAuth token represents an installation, not an authenticated human with authority to enter the merchant's provider account.
6. Reconcile after browser exit#
onExit means only that the browser left Account Onboarding. It does not prove submission, provider authentication, or readiness. Read state immediately, then poll at 2, 4, 8, and 15 seconds, followed by every 30 seconds for up to 15 minutes.
Stop polling when Flint returns a machine-completable step, another browser launch, complete, or a terminal error. If only pending_verification remains, wait. Do not mint another session just because review is asynchronous. merchant.readiness.updated is an at-least-once hint to fetch current state sooner, not a complete readiness payload.
Targeted remediation#
The same flow handles later requirements. Use an API key, read onboarding state after merchant.readiness.updated, and follow next_step.launch. When a next action names selected requirements, send the Flint IDs exactly:
curl -X POST https://api.withflintpay.com/v1/merchant-account-sessions \
-H "Authorization: Bearer $FLINT_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: remediation-business-website-example" \
-d '{
"components": ["account_onboarding"],
"collection_strategy": "incremental",
"future_requirements": "omit",
"targeted_requirement_ids": ["business_website"]
}'
Flint maps public requirement IDs to provider collection options. If an ID is unknown or cannot be mapped, Flint fails closed before returning a client secret. Never catch that error and broaden the session to collect everything. Fetch new state and show the corrected action.
Account Management is the general business and compliance surface. Notification Banner exposes ongoing risk and compliance actions. Account Onboarding is the focused collection surface. Payout destination changes after initial onboarding use the Payouts component.
Provider-originated email actions#
Ordinary headless onboarding can stay in your authenticated UI. Provider-originated compliance, risk, payout, payment, account, and document emails always route through Flint's authenticated action gateway because those links are configured at the platform level. The recipient signs in to Flint, the provider reference is resolved server-side and removed from the browser URL, and Flint mints the authorized embedded component.
The primary owner must retain a Flint login even when the rest of the integration is headless. Do not attempt to intercept or rewrite provider email links into your own session flow.
Agent loop#
- Start setup and wait for the email code.
- Verify email and store the onboarding session token.
- Read state.
- If
can_issue_api_keyis true, create and store the sandbox key without waiting for verification. - If
next_step.machine_completableis true, submit it and drive from the returned state. - If
next_step.launchexists, require the account owner, create the embedded session, and wait for browser exit. - Poll state. Wait when only review is pending.
- Once the key exists, use it for normal
/v1/...work and future remediation.
Next steps#
- CLI:
flint signupruns this flow end to end, if a terminal is an acceptable place to complete it - Onboarding API Reference
- Merchant Account Sessions
- Sandboxes API Reference
- API Keys
- Testing
- Going Live
