> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doola.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Authenticate with the raw API key in the Authorization header. Never add a Bearer prefix.
> Use the sandbox base URL https://api.test.doola.com and a dk_test_ key in examples unless the user asks for production.
> Send an Idempotency-Key header on POST /v1/partner/customers and POST /v1/partner/companies. No other endpoint takes one.
> Every response is wrapped as {payload, error}. Read fields from payload and branch on error.code, never on the message.
> Drive formation progress from webhooks plus a reconciliation sweep, never a polling loop.
> Embedded SDK (@doola/js): the dk_ key stays on the server and mints customer sessions; the browser gets only a pk_ key and the session. onFormed carries only companyId and can fire more than once. Before charging, read the company with the dk_ key, require formationSubmissionStatus AWAITING_PAYMENT, check the owner's email, and price on the server. Then POST payment-confirmed, which answers 200 with an empty body, and replace the SDK element with a new doola.create() (never append a second one).
> The doola Formation MCP server at https://mcp.doola.com is for founders forming their own company with their own doola account. Partners integrate the Partner API or the Embedded SDK. The /mcp endpoint on this docs site only searches these docs.
> For a full capability summary with the rules above, read https://docs.doola.com/skill.md.

# Go-live checklist

> Everything to check before you switch the SDK to live keys.

Work through this list in your test environment first, then once more with live keys before you open the flow to customers.

## Keys and environment

* [ ] Your page uses your `pk_live_` publishable key, and your server uses your `dk_live_` secret key. They come as a pair.
* [ ] The secret key is in your server's environment only. Search your built frontend for `dk_live_`, and expect nothing.
* [ ] Branding is set in your **production** Partner Portal account. Sandbox and production are separate accounts.
* [ ] Your CSP allows `script-src https://js.doola.com` and `frame-src https://sdk.doola.com`, and the `doola-js` Trusted Types policy if you enforce Trusted Types.

## Sessions

* [ ] The session route reads the customer from your own sign-in, never from the request body.
* [ ] It answers 401 only when nobody is signed in, and turns doola's 401 into a 502. `@doola/js/server` does both.
* [ ] It accepts `POST` only, applies your CSRF protection, and sends `Cache-Control: no-store`.
* [ ] It sends `externalCustomerId`, so a customer who changes their email with you stays the same customer.
* [ ] It answers doola's 409 with `{ code }`, and `fetchAccessToken` rejects with that `code`. `@doola/js/server` does the first.
* [ ] You log the doola error code from failed sessions on your server.

## Payment

* [ ] `onFormed` opens your checkout, and opening it twice for the same company creates one order.
* [ ] Before charging, your server checks that the company belongs to the signed-in customer and is still `AWAITING_PAYMENT`. `CANCELLED`, `FAILED` and unknown statuses are refused, never shown as paid.
* [ ] Your server prices the formation. Nothing about the price comes from the browser.
* [ ] You keep one order per `companyId`, and each charge attempt has its own idempotency key, so a retry after a declined card still goes through.
* [ ] You confirm payment only after the charge succeeds, and retry the confirmation on a network error or a 5xx.
* [ ] `409 E_COMPANY_CANCELLED` refunds the founder.
* [ ] After confirming, you replace the SDK element with a new `doola.create()`, so the founder sees their company and the old payment screen is gone.
* [ ] A scheduled sweep confirms charged orders that were never confirmed.

## After payment

* [ ] Your **production** webhook URL and signing secret are set in the Partner Portal, and your endpoint verifies signatures.
* [ ] You handle `company_formation_submitted`, `company_formation_completed`, `company_formation_failed` and `company_ein_issued`.
* [ ] You bring the founder back to the SDK on `signature_ss4_reminder_due` and `signature_form8821_reminder_due`.
* [ ] You handle `company_name_options_required`. The founder can send new names in the SDK, and your answer through the [Required actions](/api/required-actions) API replaces theirs.
* [ ] doola has every domain that hosts the SDK, so signing renders inside it.

## On the page

* [ ] Your page shows its own fallback when `loadDoola()` rejects, and when `onLoadError` reports `render_error`.
* [ ] `partner_session_expired` sends the user to your sign-in.
* [ ] `email_in_use` gives the founder a way forward, such as a different email or your support.
* [ ] `external_id_conflict` alerts your team, since only a fix to your user id mapping resolves it, and `customer_revoked` offers your support.
* [ ] You log `mint_failed` and alert when your session route keeps failing. The iframe offers the founder **Try again** itself.
* [ ] You call `doola.destroy()` when your user signs out, and never on an ordinary route change.
* [ ] You keep one SDK instance per page, so callbacks reach the screen that is mounted now.
* [ ] You tested on Chrome, Firefox, Safari and iOS Safari, and on a phone-sized screen.

## In production

* [ ] You never create test formations with live keys. Every confirmed live formation is filed with the state.
* [ ] Your team knows how to reach doola to cancel an unpaid formation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.