# Prepare a filing period's report

> The filing_periods.prepare operation: POST /api/v1/filing_periods/{id}/prepare in the REST API and the filing_periods_prepare tool in the MCP server.

Generates the period's report (spec §21.2) from the entity's invoices and credit notes and its ledger, never
from processor data, for every brand of the entity, and stores it on the period with its CSV and XLSX files.
The period becomes `prepared`. The report follows the regime (`reportKind`):

- `eu_oss`: taxable amount and VAT per member state of consumption and rate in EUR (ECB rate of the period's
  last day, or of the next publication day), corrections of earlier periods by original period, and the
  balance per member state;
- `eu_domestic`: domestic supplies by rate, corrections, the return values by rate, and the EC Sales List
  (reverse-charged services to businesses in other member states, per customer VAT ID);
- `uk_vat`: the nine boxes of the UK VAT return, with sales by rate;
- `ch_mwst`: turnover and tax by rate in CHF;
- `us_state`: gross, taxable and exempt sales and the tax by state;
- `generic`: sales and tax by jurisdiction and rate in the regime's currency.

Except for `eu_oss`, other currencies are converted per the filing spec's exchange-rate rule: the ECB rate of
the period's last day when it names it, else of each document's tax-point date. Credit notes for sales
reported in an earlier period are corrections of that period. Sales made during a grace period are reported
in the first return after the registration's effective date.

Every report includes a reconciliation: its tax per jurisdiction and currency must equal the movement of the
regime's tax payable accounts in the ledger for the period. If they differ, nothing is stored and the call
fails with `filing_reconciliation_failed`, listing the documents that differ.

Works on a `locked` period, and again on a `prepared` one until it is submitted. Write tier: tax managers,
finance, owners and admins. Dry-run it to see the report without storing anything.

## At a glance

- **REST:** `POST /api/v1/filing_periods/{id}/prepare`
- **MCP tool:** `filing_periods_prepare`
- **Operation ID:** `filing_periods.prepare`
- **Scope:** Legal entity
- **Permission:** `filings:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `filings:write` scope
- **Since:** M8

## Request

### Path parameters

| Field | Type      | Description                 |
| ----- | --------- | --------------------------- |
| `id`  | `fp_…` ID | The filing period (`fp_…`). |

### Body

A JSON object (`Content-Type: application/json`):

| Field  | Type   | Required | Description                                                    |
| ------ | ------ | -------- | -------------------------------------------------------------- |
| `note` | string | No       | A note for the period's history, e.g. who checked the figures. |

## Response

`200 OK` with a JSON object:

| Field                      | Type                                                                       | Description                                                  |
| -------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `id`                       | `fp_…` ID                                                                  |                                                              |
| `object`                   | `"filing_period"`                                                          |                                                              |
| `entityId`                 | `ent_…` ID                                                                 |                                                              |
| `livemode`                 | boolean                                                                    |                                                              |
| `regimeId`                 | string                                                                     |                                                              |
| `registrationId`           | `regn_…` ID                                                                |                                                              |
| `reportKind`               | one of `eu_oss`, `eu_domestic`, `uk_vat`, `ch_mwst`, `generic`, `us_state` |                                                              |
| `reportAvailable`          | boolean                                                                    |                                                              |
| `frequency`                | one of `monthly`, `quarterly`, `annual`                                    |                                                              |
| `periodKey`                | string                                                                     | The period: "2026-Q3", "2026-07" or "2026".                  |
| `periodStart`              | date-time                                                                  |                                                              |
| `periodEnd`                | date-time                                                                  | The end (exclusive): the next period's first local midnight. |
| `firstDay`                 | string                                                                     |                                                              |
| `lastDay`                  | string                                                                     |                                                              |
| `timeZone`                 | string                                                                     |                                                              |
| `currency`                 | string                                                                     |                                                              |
| `dueDate`                  | string                                                                     |                                                              |
| `dueAt`                    | date-time                                                                  | 23:59:59 local time on the due date.                         |
| `lockableAt`               | date-time                                                                  |                                                              |
| `status`                   | one of `open`, `locked`, `prepared`, `submitted`, `paid`                   |                                                              |
| `statusChangedAt`          | date-time                                                                  |                                                              |
| `lockedAt`                 | date-time or null                                                          |                                                              |
| `preparedAt`               | date-time or null                                                          |                                                              |
| `submittedAt`              | date-time or null                                                          |                                                              |
| `reference`                | string or null                                                             |                                                              |
| `paidAt`                   | date-time or null                                                          |                                                              |
| `paymentRef`               | string or null                                                             |                                                              |
| `totals`                   | object or null                                                             |                                                              |
| `createdAt`                | date-time                                                                  |                                                              |
| `updatedAt`                | date-time                                                                  |                                                              |
| `registration`             | object or null                                                             |                                                              |
| `endsBeforeRegistration`   | boolean                                                                    |                                                              |
| `report`                   | object or null                                                             |                                                              |
| `files`                    | array of object                                                            |                                                              |
| `files[].format`           | one of `csv`, `xlsx`, `xml`                                                |                                                              |
| `files[].fileId`           | `file_…` ID                                                                |                                                              |
| `files[].filename`         | string                                                                     |                                                              |
| `files[].contentType`      | string                                                                     |                                                              |
| `files[].size`             | integer                                                                    |                                                              |
| `files[].sha256`           | string                                                                     |                                                              |
| `receipts`                 | array of object                                                            |                                                              |
| `receipts[].kind`          | one of `file`, `reference`                                                 |                                                              |
| `receipts[].reference`     | string                                                                     |                                                              |
| `receipts[].label`         | string or null                                                             |                                                              |
| `receipts[].addedAt`       | date-time                                                                  |                                                              |
| `receipts[].addedBy`       | string                                                                     |                                                              |
| `paymentLedgerTxnIds`      | array of `txn_…` ID                                                        |                                                              |
| `reminders`                | array of object                                                            |                                                              |
| `reminders[].code`         | string                                                                     |                                                              |
| `reminders[].at`           | date-time                                                                  |                                                              |
| `reminders[].skipped`      | boolean                                                                    |                                                              |
| `history`                  | array of object                                                            |                                                              |
| `history[].from`           | one of `open`, `locked`, `prepared`, `submitted`, `paid` or null           |                                                              |
| `history[].to`             | one of `open`, `locked`, `prepared`, `submitted`, `paid`                   |                                                              |
| `history[].at`             | date-time                                                                  |                                                              |
| `history[].by`             | string                                                                     |                                                              |
| `history[].note`           | string or null                                                             |                                                              |
| `actions`                  | object                                                                     |                                                              |
| `actions.lock`             | object                                                                     |                                                              |
| `actions.lock.allowed`     | boolean                                                                    |                                                              |
| `actions.lock.code`        | string or null                                                             |                                                              |
| `actions.lock.message`     | string or null                                                             |                                                              |
| `actions.prepare`          | object                                                                     |                                                              |
| `actions.prepare.allowed`  | boolean                                                                    |                                                              |
| `actions.prepare.code`     | string or null                                                             |                                                              |
| `actions.prepare.message`  | string or null                                                             |                                                              |
| `actions.submit`           | object                                                                     |                                                              |
| `actions.submit.allowed`   | boolean                                                                    |                                                              |
| `actions.submit.code`      | string or null                                                             |                                                              |
| `actions.submit.message`   | string or null                                                             |                                                              |
| `actions.markPaid`         | object                                                                     |                                                              |
| `actions.markPaid.allowed` | boolean                                                                    |                                                              |
| `actions.markPaid.code`    | string or null                                                             |                                                              |
| `actions.markPaid.message` | string or null                                                             |                                                              |

## Examples

### Generate the Q3 OSS return

```http
POST /api/v1/filing_periods/fp_01m4tx5nj8emwbyf7y9vcf47y0/prepare
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
```

Response (`200`):

```json
{
  "id": "fp_01m4tx5nj8emwbyf7y9vcf47y0",
  "object": "filing_period",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "livemode": true,
  "regimeId": "EU_OSS_UNION",
  "registrationId": "regn_01m4tx5nj8emwbyf7y9vcf47xk",
  "reportKind": "eu_oss",
  "reportAvailable": true,
  "frequency": "quarterly",
  "periodKey": "2026-Q3",
  "periodStart": "2026-06-30T22:00:00.000Z",
  "periodEnd": "2026-09-30T22:00:00.000Z",
  "firstDay": "2026-07-01",
  "lastDay": "2026-09-30",
  "timeZone": "Europe/Zagreb",
  "currency": "EUR",
  "dueDate": "2026-10-31",
  "dueAt": "2026-10-31T22:59:59.000Z",
  "lockableAt": "2026-09-30T22:00:00.000Z",
  "status": "prepared",
  "statusChangedAt": "2026-10-02T08:05:00.000Z",
  "lockedAt": "2026-10-02T08:00:00.000Z",
  "preparedAt": "2026-10-02T08:05:00.000Z",
  "submittedAt": null,
  "reference": null,
  "paidAt": null,
  "paymentRef": null,
  "totals": {
    "currency": "EUR",
    "taxable": "1500000000",
    "tax": "290000000",
    "corrections": "-19000000",
    "due": "271000000",
    "refundable": "0"
  },
  "createdAt": "2026-07-01T03:00:00.000Z",
  "updatedAt": "2026-10-02T08:05:00.000Z",
  "registration": {
    "id": "regn_01m4tx5nj8emwbyf7y9vcf47xk",
    "state": "registered",
    "registrationNumber": "HR12345678901",
    "effectiveFrom": "2026-03-31T22:00:00.000Z"
  },
  "endsBeforeRegistration": false,
  "report": {
    "kind": "eu_oss",
    "currency": "EUR",
    "rounding": "half_up",
    "fxRule": null,
    "generatedAt": "2026-10-02T08:05:00.000Z",
    "fx": [],
    "sections": [
      {
        "id": "supplies",
        "title": "Supplies by member state of consumption and rate",
        "columns": [
          {
            "key": "memberState",
            "label": "Member state",
            "type": "text"
          },
          {
            "key": "rate",
            "label": "VAT rate",
            "type": "rate"
          },
          {
            "key": "rateKind",
            "label": "Rate type",
            "type": "text"
          },
          {
            "key": "taxable",
            "label": "Taxable amount",
            "type": "money"
          },
          {
            "key": "vat",
            "label": "VAT",
            "type": "money"
          },
          {
            "key": "lines",
            "label": "Lines",
            "type": "count"
          }
        ],
        "rows": [
          {
            "memberState": "DE",
            "rate": "190000",
            "rateKind": "standard",
            "taxable": "1000000000",
            "vat": "190000000",
            "lines": 8
          },
          {
            "memberState": "FR",
            "rate": "200000",
            "rateKind": "standard",
            "taxable": "500000000",
            "vat": "100000000",
            "lines": 3
          }
        ]
      },
      {
        "id": "corrections",
        "title": "Corrections of earlier periods",
        "columns": [
          {
            "key": "originalPeriod",
            "label": "Period corrected",
            "type": "text"
          },
          {
            "key": "memberState",
            "label": "Member state",
            "type": "text"
          },
          {
            "key": "taxable",
            "label": "Taxable amount",
            "type": "money"
          },
          {
            "key": "vat",
            "label": "VAT",
            "type": "money"
          },
          {
            "key": "lines",
            "label": "Lines",
            "type": "count"
          }
        ],
        "rows": [
          {
            "originalPeriod": "2026-Q2",
            "memberState": "DE",
            "taxable": "-100000000",
            "vat": "-19000000",
            "lines": 1
          }
        ]
      },
      {
        "id": "balances",
        "title": "VAT due per member state",
        "columns": [
          {
            "key": "memberState",
            "label": "Member state",
            "type": "text"
          },
          {
            "key": "supplies",
            "label": "VAT on supplies",
            "type": "money"
          },
          {
            "key": "corrections",
            "label": "Corrections",
            "type": "money"
          },
          {
            "key": "balance",
            "label": "Balance",
            "type": "money"
          }
        ],
        "rows": [
          {
            "memberState": "DE",
            "supplies": "190000000",
            "corrections": "-19000000",
            "balance": "171000000"
          },
          {
            "memberState": "FR",
            "supplies": "100000000",
            "corrections": "0",
            "balance": "100000000"
          }
        ]
      },
      {
        "id": "reconciliation",
        "title": "Reconciliation with the ledger",
        "columns": [
          {
            "key": "jurisdiction",
            "label": "Jurisdiction",
            "type": "text"
          },
          {
            "key": "currency",
            "label": "Currency",
            "type": "text"
          },
          {
            "key": "reportTax",
            "label": "Report tax",
            "type": "money"
          },
          {
            "key": "ledgerTax",
            "label": "Ledger tax payable",
            "type": "money"
          },
          {
            "key": "difference",
            "label": "Difference",
            "type": "money"
          }
        ],
        "rows": [
          {
            "jurisdiction": "DE",
            "currency": "EUR",
            "reportTax": "171000000",
            "ledgerTax": "171000000",
            "difference": "0"
          },
          {
            "jurisdiction": "FR",
            "currency": "EUR",
            "reportTax": "100000000",
            "ledgerTax": "100000000",
            "difference": "0"
          }
        ]
      }
    ],
    "reconciliation": {
      "matches": true,
      "rows": [
        {
          "jurisdictionId": "DE",
          "currency": "EUR",
          "reportTax": "171000000",
          "ledgerTax": "171000000",
          "difference": "0"
        },
        {
          "jurisdictionId": "FR",
          "currency": "EUR",
          "reportTax": "100000000",
          "ledgerTax": "100000000",
          "difference": "0"
        }
      ]
    },
    "counts": {
      "lines": 12,
      "corrections": 1,
      "notReported": 0,
      "documents": 12
    }
  },
  "files": [
    {
      "format": "csv",
      "fileId": "file_01m4tx5nj8emwbyf7y9vcf47y2",
      "filename": "EU_OSS_UNION-2026-Q3.csv",
      "contentType": "text/csv; charset=utf-8",
      "size": 2381,
      "sha256": "5d1c9d6b7f3e0a4c8b2f1e6d9a7c3b5e0f4d8a2c6b1e9f7d3a5c0b8e2f6d4a1c"
    },
    {
      "format": "xlsx",
      "fileId": "file_01m4tx5nj8emwbyf7y9vcf47y3",
      "filename": "EU_OSS_UNION-2026-Q3.xlsx",
      "contentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "size": 9184,
      "sha256": "8a3f6c1e9d4b7a2f5c0e8d3b6a1f4c9e7d2b5a0f3c8e6d1b4a9f7c2e5d0b3a6f"
    }
  ],
  "receipts": [],
  "paymentLedgerTxnIds": [],
  "reminders": [],
  "history": [
    {
      "from": null,
      "to": "open",
      "at": "2026-07-01T03:00:00.000Z",
      "by": "system",
      "note": null
    },
    {
      "from": "open",
      "to": "locked",
      "at": "2026-10-02T08:00:00.000Z",
      "by": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
      "note": null
    },
    {
      "from": "locked",
      "to": "prepared",
      "at": "2026-10-02T08:05:00.000Z",
      "by": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
      "note": null
    }
  ],
  "actions": {
    "lock": {
      "allowed": false,
      "code": "filing_period_status_invalid",
      "message": "This period is prepared: \"lock\" needs open."
    },
    "prepare": {
      "allowed": true,
      "code": null,
      "message": null
    },
    "submit": {
      "allowed": true,
      "code": null,
      "message": null
    },
    "markPaid": {
      "allowed": false,
      "code": "filing_period_status_invalid",
      "message": "This period is prepared: \"mark_paid\" needs submitted."
    }
  }
}
```

## Errors

| Code                                                                                                                   | HTTP status | When                                                                                                                                                                                  | What to do                                                                                                                                                                                                                   |
| ---------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                                 | 400         | A field is malformed (`param` names it).                                                                                                                                              | Fix the fields listed in `details.issues` and send the request again.                                                                                                                                                        |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                               | 404         | No filing period with this ID exists for this legal entity and mode.                                                                                                                  | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                                         |
| [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required)                                       | 400         | The request names no legal entity (an org key without `Ownrecord-Entity`).                                                                                                            | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (`details.required_scope`).                                                                           |
| [`filing_period_status_invalid`](https://ownrecord.io/docs/reference/errors.md#filing_period_status_invalid)           | 409         | The period isn't `locked` or `prepared` (`details.status`).                                                                                                                           | Reload the period and check its `status`: `details.allowed_from` lists the statuses the action needs. Periods go open → locked → prepared → submitted → paid.                                                                |
| [`filing_report_kind_unsupported`](https://ownrecord.io/docs/reference/errors.md#filing_report_kind_unsupported)       | 409         | Ownrecord can't generate this regime's report yet (`details.report_kind`).                                                                                                            | Prepare the return outside Ownrecord for now; the report for this kind of regime comes in a later release.                                                                                                                   |
| [`filing_registration_not_effective`](https://ownrecord.io/docs/reference/errors.md#filing_registration_not_effective) | 409         | The registration is still pending: grace sales have no return until it has an effective date.                                                                                         | Mark the registration registered with its number and effective date first: sales made during the grace period are reported in the first return after it.                                                                     |
| [`fx_rate_unavailable`](https://ownrecord.io/docs/reference/errors.md#fx_rate_unavailable)                             | 404         | The ECB rates the report converts with (of the period's last day or the next publication day, or of a document's tax-point date) aren't loaded yet. Retry after the next publication. | Check the currency (the ECB publishes about 30) and the date. Rates are fetched every day after 16:00 CET; for a date with no rate yet, try again after the next publication.                                                |
| [`filing_reconciliation_failed`](https://ownrecord.io/docs/reference/errors.md#filing_reconciliation_failed)           | 409         | The report's tax differs from the ledger's tax payable (`details.rows`, `details.documents`).                                                                                         | Check the documents in `details.documents`: each shows the report's and the ledger's tax. Correct the ledger or the documents (for example with a manual ledger adjustment or a credit note), then prepare the period again. |
| [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout)                                   | 502         | The file store didn't answer in time; nothing was stored. Retry.                                                                                                                      | Retry the request with the same Idempotency-Key after a short wait.                                                                                                                                                          |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                                   | 409         | The period changed at the same moment. Reload it and try again.                                                                                                                       | Fetch the object again and retry the change.                                                                                                                                                                                 |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                                 | 403         | You can't work on this entity's filings (tax managers, finance, owners and admins can).                                                                                               | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                                              |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
