> ## 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.

# Security and data

> What data goes where, how sessions are scoped, and what your security team needs to allow.

The SDK is built so that the most sensitive data in a formation never enters your systems, and so that nothing in the browser can change what you charge or what doola files.

## Where data goes

| Data | Path | Reaches your systems? |
| - | - | - |
| Founder's name, phone, addresses, ownership | Typed into the iframe, sent from there to doola | No |
| SSNs and ITINs | Typed into the iframe, sent from there to doola | No. Never in your page's JavaScript, error tracking, session replay or logs |
| Signatures on IRS forms | Collected by doola's signing provider inside the iframe | No |
| The company id | Sent to your page through `onFormed` | Yes, as an untrusted hint |
| Company and customer details | Read by your server with your secret key | Yes, when you ask for them |
| Your customer's email and id | Sent by your server when it mints a session | You send them |

The iframe is served from doola's origin, so the browser's same-origin policy keeps your page's scripts out of it, and its scripts out of your page. The two sides only exchange a small set of validated messages, and each side checks where every message comes from before acting on it.

## Sessions

* **Your secret key never reaches a browser.** The loader refuses anything but a publishable key, and `@doola/js/server` refuses a publishable one.
* **A session acts as one customer only.** The `cs_` token can read and submit that customer's formation through the SDK, and nothing else: no other customer, and no Partner API endpoint.
* **Sessions last 10 minutes** and are renewed through your route while the SDK is mounted. When your user signs out, your route answers 401 and the SDK stops.
* **The token stays in memory.** The SDK never puts it in a URL, a cookie or browser storage.

## Trust nothing from the browser

`onFormed` hands your page a `companyId` and nothing else, on purpose. Anything that reaches your page's JavaScript can be changed by the person using it. So your server:

* reads the company with your secret key and checks it is `AWAITING_PAYMENT`;
* checks it belongs to the signed-in customer;
* prices it from what doola returns, never from a value sent by the browser.

See [Take payment](/sdk/payments).

## What is stored in the browser

The iframe stores two things in its own origin's local storage, and nothing else:

* **The founder's unfinished wizard,** so they can resume. SSNs and ITINs are blanked before it is saved, and the founder types them again on return. It expires after 7 days and is deleted when they submit.
* **A short-lived marker** that your checkout was opened, used only to decide when to show the founder a "Continue to payment" button. It expires after 10 minutes.

Browsers that block storage in iframes keep the draft in memory instead, so it lasts until the founder leaves the page.

## Content Security Policy

If your site sends a CSP, allow the loader script and the iframe:

```text theme={null}
script-src https://js.doola.com;
frame-src https://sdk.doola.com;
```

* With test keys, also allow `https://sdk.test.doola.com` in `frame-src`.
* With your [own domain](/sdk/presentation#your-own-domain), allow that origin in `frame-src` instead.
* No `connect-src` or `style-src` change is needed: the session request goes to your own route, and the loader sets its styles through the CSSOM.

If you enforce Trusted Types (`require-trusted-types-for 'script'`), also allow the `doola-js` policy. It accepts only the loader URL.

```text theme={null}
trusted-types doola-js;
```

If you already send a `trusted-types` directive, add `doola-js` to its list.

## Other headers

* **`Cross-Origin-Opener-Policy: same-origin`** is fine. The SDK opens new tabs with plain links, so its fallbacks keep working under it.
* **`X-Frame-Options` and `frame-ancestors`** on your own pages do not affect the SDK. They control who can frame you, not what you frame.
* The iframe gets `allow="clipboard-write"` so founders can copy values, and no other permission.

## The loader

`@doola/js` loads `https://js.doola.com/v1/doola.js` with `crossorigin="anonymous"`. That loader is versioned by major (`/v1`) and updated in place by doola, so security fixes reach your site without a deploy. It never changes the shape of the options you pass, within a major version.

## Report a vulnerability

Email [security@doola.com](mailto:security@doola.com). doola acknowledges reports within two business days. Please never report a vulnerability in a public issue.


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