# Download all of your company's data

> Take every record and file Oatmilk keeps for your company, and close the company when you're done.

Source: https://app.getoatmilk.com/docs/tutorials/export-company-data

Your company's data is yours. This tutorial downloads all of it with an administrator's API key that holds `accounting:admin`: every record Oatmilk keeps in every table, and every stored file. In the web app it's **Settings › Company › Your data**, and in the terminal app it's `oatmilk export`.

## 1. Build the export

`company.export` builds one ZIP and returns a link to it that works for five minutes.

```bash
curl https://app.getoatmilk.com/api/v1/accounting/company.export \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "includeFiles": true
}'
```

The ZIP holds:

- `manifest.json`: every table with its number of rows, every stored file with its size and whether it's in this download, and the columns left out because they hold secrets.
- `data/<table>.json`: the company's rows in each table, one row per line, with the database's column names. Amounts are in cents, the currency's smallest unit.
- `files/<bucket>/<path>`: the stored files, such as receipts, emails, statements, agreements and invoices.
- `README.txt`: the same layout in plain words.

Encrypted credentials, bank and tax details kept encrypted, one-time codes and private link tokens read `"[redacted]"`.

## 2. Download the files that didn't fit

One download holds at most 50 MB. The records come first and files fill the rest, so a company with many files gets some of them listed with `"included": false` and counted in `files.skipped`. `company.export.files` lists every file with its own five-minute link, 100 at a time.

```bash
curl -G https://app.getoatmilk.com/api/v1/accounting/company.export.files \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  --data-urlencode 'offset=0' \
  --data-urlencode 'limit=100'
```

```js title="download-files.js"
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";

for (let offset = 0; offset !== null; ) {
  const page = await callOatmilk("company.export.files", { offset, limit: 100 });
  for (const file of page.items) {
    const target = join("oatmilk-files", file.bucket, file.path);
    await mkdir(dirname(target), { recursive: true });
    await writeFile(target, Buffer.from(await (await fetch(file.url)).arrayBuffer()));
  }
  offset = page.nextOffset;
}
```

## Closing the company

`company.close` permanently deletes the company: its stored files, every record, and the company itself with its memberships. It can't be undone, so export first. `confirmName` must be the company's name. Only an administrator can close it, with their own credential: the web app, the terminal app or their own API key with `accounting:write` and `accounting:admin`. AI apps and Ask AI are refused with `INTERACTIVE_ADMIN_REQUIRED`, and the company an Oatmilk installation runs for can't be closed.
