# Upload a task document

> The tasks.documents.upload operation: POST /api/v1/tasks/{id}/documents in the REST API and the tasks_documents_upload tool in the MCP server.

Uploads a receipt for a task: the tax portal's confirmation, a screenshot, the authority's letter. Send the
file base64-encoded in `content`: a PDF, PNG, JPEG or WebP file of at most 3 MB, with its `filename`. The
type is read from the file itself. The document is stored privately (spec §7.10) with the task's filing
period or registration, and returned with its `file_…` ID: pass it in `outputs.files` of `tasks.submit` (or
`tasks.complete`). Uploading the same file again returns the same document.

Write tier. Dry-run it to check a file without storing it.

## At a glance

- **REST:** `POST /api/v1/tasks/{id}/documents`
- **MCP tool:** `tasks_documents_upload`
- **Operation ID:** `tasks.documents.upload`
- **Scope:** Organization, or one of its legal entities or brands
- **Permission:** `tasks: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 `tasks:write` scope
- **Since:** M10

## Request

### Path parameters

| Field | Type        | Description |
| ----- | ----------- | ----------- |
| `id`  | `task_…` ID |             |

### Body

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

| Field         | Type                                                              | Required | Description                                                                      |
| ------------- | ----------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------- |
| `content`     | string                                                            | Yes      | The file's bytes, base64-encoded: a PDF, PNG, JPEG or WebP file of at most 3 MB. |
| `filename`    | string                                                            | No       | The file's name.                                                                 |
| `contentType` | one of `application/pdf`, `image/png`, `image/jpeg`, `image/webp` | No       | The file's type. Optional: it is read from the file, and must agree when given.  |

## Response

`201 Created` with a JSON object:

| Field         | Type                 | Description |
| ------------- | -------------------- | ----------- |
| `id`          | `file_…` ID          |             |
| `object`      | `"task_document"`    |             |
| `taskId`      | `task_…` ID          |             |
| `ownerId`     | string               |             |
| `filename`    | string or null       |             |
| `contentType` | string               |             |
| `size`        | integer (at least 0) |             |
| `sha256`      | string               |             |
| `createdAt`   | date-time            |             |

## Examples

### Upload the OSS portal's confirmation

```http
POST /api/v1/tasks/task_01m4ttwd3veh9apbewxheq5xa9/documents
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "content": "JVBERi0xLjQKJcOkw7zDtsOfCjEgMCBvYmoKPDwvVHlwZS9DYXRhbG9nPj4KZW5kb2JqCnRyYWlsZXIKPDwvUm9vdCAxIDAgUj4+CiUlRU9GCg==",
  "filename": "oss-q3-confirmation.pdf"
}
```

Response (`201`):

```json
{
  "id": "file_01m4ttwd3veh9apbewxheq5xah",
  "object": "task_document",
  "taskId": "task_01m4ttwd3veh9apbewxheq5xa9",
  "ownerId": "fp_01m4ttwd3veh9apbewxheq5xa7",
  "filename": "oss-q3-confirmation.pdf",
  "contentType": "application/pdf",
  "size": 48213,
  "sha256": "4f1a9c0d7e2b6a3f8c5d1e9b0a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f",
  "createdAt": "2026-10-15T09:25:00.000Z"
}
```

## Errors

| Code                                                                                                 | HTTP status | When                                                               | What to do                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)               | 400         | The content isn't a PDF, PNG, JPEG or WebP file of at most 3 MB.   | 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 task with this ID in this organization, entity and mode.        | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                                             |
| [`task_input_not_available`](https://ownrecord.io/docs/reference/errors.md#task_input_not_available) | 409         | The task has no registration or filing period.                     | Read `details.reason`: a registration packet belongs to registration tasks, a filing package to filing tasks once the period is prepared (`filing_periods.prepare`), and a playbook exists only where the tax knowledge has one. |
| [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout)                 | 502         | The file store didn't answer in time; nothing was recorded. Retry. | Retry the request with the same Idempotency-Key after a short wait.                                                                                                                                                              |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)               | 403         | You can't work on compliance tasks here.                           | 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).
