GET /onboarding reads the current setup. PATCH /onboarding saves answers, submits them for a decision, or changes the ready-email preference. The MCP equivalents are get_onboarding and update_onboarding.
Both endpoints require Company: Manage, Integrations: Manage, and the usual API access: Manage. MCP writes also require finta:write; a read-only connection cannot submit answers. Onboarding calls never consume the monthly API or MCP allowance, including after setup is complete. Existing authentication, billing and burst checks still apply.
Read current setup
object: "onboarding" with status, saved answers, remaining questions, the eligibility decision, selected integrations, first-import sync progress, notify_when_synced, and next_step.
decision is null until Finta decides. Its outcome is approved (Finta can onboard the company), call_required (a call is needed first), or not_eligible (Finta cannot onboard it). reason explains only a not-eligible outcome and is null otherwise. decided is the Unix decision timestamp, or null for older decisions without a recorded time.
Follow the next step
next_step.message is written for the agent. On an MCP connection it names the MCP tools (update_onboarding, get_onboarding, connect_integration); with an API key it names the endpoints. url is present only with book_call or finish_in_web and email only with contact_support.
For a new setup, open with: “Welcome to Finta! A few company questions, then connect your banks and we’ll set up your books.”
Save what you can already answer from chat, local files or connected tools within the user’s permissions. Do not re-ask known answers or guess without a source; conflicting sources count as unknown. Ask only missing questions, two or three related questions at a time, and save quietly.
For questions with options, use your app’s multiple-choice question tool if available. Otherwise ask in plain text without numbered lists, showing options only if asked. Map the user’s answer to the accepted values. Banks and cards have no displayed options: gather them from context or ask in plain text.
Before submitting, recap every answer, naming the source of any answer you found, then stop. Send submit: true only in a later turn after a separate explicit yes to that recap. A yes selected in the multiple-choice tool counts; an earlier confirmation does not. Tool descriptions are brief: follow the returned next_step.message for the conversation and connection workflow.
Questions and answers
The API names differ from the web form’s in places. Each answer isnull (or an empty list) until answered.
Text answers are up to 500 characters. The web app’s “No bank yet” and “No credit cards yet” options aren’t offered for new API selections. GET can return their saved values (
no_bank_yet and no_credit_cards_yet). PATCH accepts an unchanged saved bank/card pair containing them, before or after the decision; a new or changed selection containing them returns 400 invalid_request. Both the OpenAPI request enums and MCP input enums include these values to permit unchanged replay; they remain unavailable for new selections.
New answers can use canonical provider keys directly. These answers save the same values as banks: ["other"], other_bank: "Bank of America" and credit_cards: ["other"], other_credit_card: "American Express":
questions lists what’s still to ask at this stage, in order: the required questions that are unanswered and, while no card is chosen, the optional credit_cards question. Each is { key, label, description?, type, required, options }, where type is single_select, multi_select, boolean, year or text, and options is a list of { value, label } (empty for banks, credit_cards, boolean, text and year). Once an answer means Finta can’t onboard the company (an S corporation or “other” entity, or a crypto business), the later questions aren’t asked. While a call is required, the follow-ups are asked only when Finta needs them: the call-history questions for a company incorporated 3 or more years ago, and subsidiary_countries for one with foreign subsidiaries.
The API and MCP show the choices below, in order. Banks and credit cards have options: [] and keep type multi_select; the agent gathers answers from context or asks in plain text and maps them to accepted values. These four questions omit description. The shorter displayed lists do not restrict the accepted input values: use the complete API request schema or MCP input enums, including other with other_bank or other_credit_card. State and year questions use their own returned choices and types.
Save and submit
Send only the answers to change. This example saves a draft without requesting a decision:- Omitted answers keep their saved values. Every request field is optional.
- The API and MCP
update_onboardinginput schema acceptnullfor every answer field, including enum answers. This lets clients replay nullable answers or clear an optional answer, such as{"answers":{"incorporation_year":null}}while that follow-up is editable. Clearing a required answer is rejected, and answer locks still apply. Theanswersobject, array items,submit, andnotify_when_synceddo not acceptnull. - An invalid answer, an unknown key, or the wrong type rejects the whole update with
400 invalid_request; nothing is saved. - Submitting with missing required answers returns
400 answers_incompleteand themissingkeys. Valid answers in that request are saved. Ask only what’s missing and submit again after confirmation. - After the decision, eligibility answers are locked (
409 onboarding_locked). While a call is required, the exactincorporation_year,accounting_software,files_taxes_with,goals, andsubsidiary_countriesfollow-ups can still change. - Repeating already-saved answers or a successful submit returns 200 with the current state. Matching an existing answer is not a change, so retrying a request is safe while onboarding is unfinished.
- After
status: "complete", every PATCH returns409 onboarding_complete; GET remains available.
Replay answers saved on the web
After a web eligibility decision, you can send GET’sanswers back unchanged without clearing the underlying web answers or triggering an answer lock:
- A null
incorporation_yearpreserves a saved web range when no exact year was answered. - An unchanged
legal_entity_statecode preserves the saved state, includingDCfor a saved legacy value. If a legacy web value cannot map to a code, echoing the returned null preserves that value too. NewDCselections are rejected. - The returned
industryis the first saved industry; echoing it preserves any additional industries saved on the web. - Bank/card pairs compare their normalized saved values, including an
otheranswer folded into a known bank or card. The returned canonical slugs can be replayed through the API or schema-enforcing MCP clients: unchanged echoes return 200 without rewriting the company or questionnaire, before or after submission. Blank text and subsidiary-country lists use their normalized comparisons too.
next_step even when no answers changed.
Connect banks and follow the import
Connection creation and status retrieval become available only after approval (status is connect_banks or syncing). Follow next_step.message: use the integration connect endpoint, or MCP connect_integration, for new OAuth and Plaid connections, including Chase and SVB. The user approves on the returned provider or Plaid Hosted Link page; then check the request with GET /integrations/{key}/connect, or get_integration_connection. A Plaid link lasts 30 minutes and needs no Finta sign-in, so share it only with the user. The return page does not confirm success; use the connect request’s status.
For a Plaid bank that needs reauthentication, follow the web get-started page instructions in next_step.message, then send PATCH /onboarding with {"answers":{}} (MCP: update_onboarding) to continue setup. This applies both before and during the first import. Hosted Link cannot reconnect an existing Plaid bank: the connect endpoint returns 409 integration_already_connected, even when the bank needs reconnection. OAuth reconnects continue through the API or MCP connect flow.
OAuth callbacks and successful Plaid Hosted Link sessions both start onboarding’s first import when the last selected bank connects. If the user connected banks in the web app instead, an empty-answer onboarding update can start the import as described below.
The first import starts automatically after the last selected bank connects through the API or MCP. If all selected banks were already connected, an approving update can start it immediately and return status: "syncing". If a later read finds every selected bank connected but the import has not started, next_step.action is start_import. Send PATCH /onboarding with {"answers":{}} (MCP: update_onboarding) to start it, then follow the returned next step; reading alone cannot start an import. API-started onboarding also finishes automatically; it does not need a dashboard button. Web-started onboarding keeps its web flow. If an agent takes over after that import finishes, next_step.action is finish_in_web: ask the user to switch to the company named in next_step.message in Finta first, then open next_step.url, choose See dashboard, and read onboarding again. A browser session can have a different company selected from the API key or MCP connection. The status stays syncing until that click; waiting alone cannot complete this web flow.
Finta automatically retries interrupted job handoffs for API import start and completion. Keep following the returned next step and paced checks.
The banks and cards the company selected that onboarding waits on, in this order: Mercury, Brex, Ramp, Rho, SVB, Chase. A bank or card typed in under “other” isn’t listed, as in the web app. Each status is:
sync is present only while status is syncing: { "stage": ..., "check_again_in_seconds": 30 }. stage is downloading (downloading transactions), categorizing, matching_transfers (matching transfers and preparing the dashboard, the last step) or failed (a bank’s sync failed; next_step names it and offers reconnection if reauthentication is required, otherwise contact_support).
Tell the user Finta is pulling in transactions and setting up the books. Wait sync.check_again_in_seconds before reading onboarding again, or 30 seconds if sync is null. Avoid a tight polling loop. Follow next_step.message to reconnect an expired connection or contact support about an import failure with an active connection. A new connection link cannot retry an import while that connection is still active.
If you created the user’s Finta account, tell them once at wait_for_sync to open the environment’s login URL in next_step.message, choose Forgot password, and set a password so they can use Finta in their browser later.
Ready email
At the import wait step, offer the ready email once if it has not already been discussed. Honor a preference the user volunteers at any time. Send{"notify_when_synced":true} with PATCH, or the same argument to update_onboarding, to email the user making that request when the first sync finishes. If it already finished, Finta sends the email right away. false withdraws the preference. It can change after at least one question is answered and until onboarding is complete.
Endpoints available during onboarding
Untilstatus: "complete", other endpoints return 403 onboarding_incomplete. This includes companies that require a call or are not eligible. These are the exceptions:
These exceptions still require their usual permissions. Other access checks, including the existing user, company-closed and subscription checks, take precedence. An unknown route remains
404 resource_missing.
This is a change for existing credentials belonging to an unfinished company: API and MCP access is now held until onboarding completes. The MCP connection stays connected and tools/list still lists all tools; gated calls return an onboarding tool error. Finish onboarding before retrying those calls.
Errors and retries
Treat unfamiliar future status, action or error values defensively. Preserve the returned message and documentation link for diagnosis rather than parsing human-readable text as a stable contract.