---
title: "Upload a task attachment"
method: POST
path: "/companies/{company}/tasks/{task}/attachments"
tags: ["Tasks"]
---

# Upload a task attachment

`POST /companies/{company}/tasks/{task}/attachments`

## Request

- Base URL: `https://api.factuarea.com/v1`
- URL: `https://api.factuarea.com/v1/companies/{company}/tasks/{task}/attachments`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Upload a file to a task as `multipart/form-data` (`file`), up to 10 MB; the type is checked by content, not extension.
- `target` places it in a new comment (default) or the description; `note` (max 1,000 characters) is the comment text.
- Counts towards the plan storage quota (402 `storage_quota_exceeded`). Requires an `Idempotency-Key`.

## Path parameters

- `company` string, uuid, required
- `task` string, uuid, required

## Headers

- `Idempotency-Key` string, required
- `Factuarea-Version` string, date

## Response `201`

- object
  - `data` TaskAttachment, required: A file attached to a task, in its description or in a comment.
    - `id` string, uuid, required: UUID v7 of the attachment.
    - `object` 'task_attachment', required: Stripe-like discriminator. Always `task_attachment` for this resource.
    - `task_id` string, uuid, required: UUID of the task.
    - `comment_id` string, uuid, nullable, required: UUID of the comment it belongs to, or `null` when attached to the description.
    - `surface` 'description' | 'comment', required
    - `file_name` string, required
    - `mime_type` 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'image/heic' | 'image/avif' | 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'application/vnd.oasis.opendocument.text' | 'application/vnd.oasis.opendocument.spreadsheet' | 'text/plain' | 'text/csv' | 'text/markdown' | 'application/zip', required: Real type of the file, detected by content on upload.
    - `size_bytes` integer, required
    - `kind` 'image' | 'file', required
    - `uploaded_by_id` string, uuid, nullable, required: UUID of the member who uploaded it, or `null`.
    - `created_at` string, date-time, required: Upload timestamp.

## Other responses

- `400`: `parameter_unknown` with `subcode` `retired_active_profile_header`: the retired `X-Active-Profile` header was sent; the company goes in the path. `parameter_invalid_format` on `Factuarea-Version`: the header is not a `YYYY-MM-DD` date. `unsupported_api_version`: the `Factuarea-Version` date is not a supported version.
- `401`: Missing or invalid API key.
- `402`: The operation requires a payment that could not be completed: either no payment method is on file (`error.details.payment_setup_url` links to the Billing Portal where it can be set up), the immediate charge was declined by the payment provider, the account lacks the plan or add-on this operation bills against, the annual document limit is reached (`plan_limit_exceeded`, with `error.details.period_end` indicating the end of the period — wait until it has ended or upgrade the plan to continue now), or the account has reached its maximum usage (`storage_quota_exceeded`, raised by upload operations such as signing a delivery note or attaching a file to an expense — contact support before retrying). Nothing was created or modified — resolve the cause and retry the same request. Version note: `error.type` is `payment_required_error` from `Factuarea-Version: 2026-09-01` onwards; earlier versions receive `invalid_request_error` for the three codes that predate that cut (`payment_method_required`, `employee_seat_payment_method_required` and `employee_seat_charge_failed`; every other 402 code, such as `addon_required`, `plan_limit_exceeded` or the additional tax ID codes, is newer and always carries `payment_required_error`). `error.code` is stable across every version.
- `403`: The request is authenticated but not allowed: the API key lacks the required scope (`insufficient_scope`), the plan does not include the public API or this feature (`addon_not_active`, `feature_not_available_in_plan`), or the module is not available with a test key (`module_not_available_in_sandbox`).
- `404`: The requested resource does not exist or belongs to another company.
- `409`: The request conflicts with data that already exists or with a request running at the same time. `resource_conflict`: another request was reserving identifiers for the same kind of record at that moment and this one could not complete its reservation. Nothing was written; send the request again after a few seconds.
- `413`: The packaged download you asked for is too large to build, so nothing was generated and no file was left on the server. Two catalog codes carry this status, both with `error.type: invalid_request_error`: `export_document_cap_exceeded` (the request covers more documents than the cap for that artifact allows — narrow the date range or export in batches) and `export_byte_cap_exceeded` (the artifact would weigh more than the byte cap; `error.subcode` says whether it was rejected up front from the size estimate, `before_writing`, or aborted mid-packaging, `while_writing`, in which case the partial file was deleted and nothing is served). Retrying the same request unchanged returns the same error — ask for less, do not wait. A third, unrelated code shares this status on every write operation: `payload_too_large`, raised when the REQUEST body exceeds the 1 MB limit, which is about what you send and not about the size of what you asked to build.
- `422`: Validation failed. The `error.param` field identifies which input is invalid.
- `429`: Rate limit exceeded. Retry after the duration in `Retry-After`.
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/factuarea/apis/factuarea-public-api.md) · [All operations](https://skmtc.dev/factuarea/apis/factuarea-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/factuarea/apis/factuarea-public-api/revisions/718e77934422?raw)
