Skip to main content
Onboarding sets up a new company’s books: answer a short questionnaire, receive Finta’s eligibility decision, connect the selected banks and cards, and wait for the first import. You can complete this through the API or an AI assistant using the MCP tools. 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

The response is an 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 is null (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":
Saved API or MCP drafts can also resume in the web app after the first step. The shared eligibility rules recognize exact years and aged ranges: a company incorporated 3 or more years ago still needs a call, while a recent exact year does not itself require one. Follow the returned decision for saved ranges whose age is unknown. 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:
Once all required questions are answered and the user has separately confirmed the recap, submit:
Both successful updates return HTTP 200 with the current onboarding object.
  • Omitted answers keep their saved values. Every request field is optional.
  • The API and MCP update_onboarding input schema accept null for 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. The answers object, array items, submit, and notify_when_synced do not accept null.
  • 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_incomplete and the missing keys. 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 exact incorporation_year, accounting_software, files_taxes_with, goals, and subsidiary_countries follow-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 returns 409 onboarding_complete; GET remains available.

Replay answers saved on the web

After a web eligibility decision, you can send GET’s answers back unchanged without clearing the underlying web answers or triggering an answer lock:
  • A null incorporation_year preserves a saved web range when no exact year was answered.
  • An unchanged legal_entity_state code preserves the saved state, including DC for a saved legacy value. If a legacy web value cannot map to a code, echoing the returned null preserves that value too. New DC selections are rejected.
  • The returned industry is the first saved industry; echoing it preserves any additional industries saved on the web.
  • Bank/card pairs compare their normalized saved values, including an other answer 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.
These unchanged echoes do not expand the choices for new answers. Actual changes still obey validation and the decision locks. An approved update can still start the first import when all selected banks are connected; follow the returned 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

Until status: "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.