---
title: "Upload a table attachment"
method: POST
path: "/v1/tables/{tableId}/attachments"
tags: ["Tables"]
---

# Upload a table attachment

`POST /v1/tables/{tableId}/attachments`

## Request

- Base URL: `https://api.banksync.io`
- URL: `https://api.banksync.io/v1/tables/{tableId}/attachments`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Stage a file for a `files` field, or — with no `fieldKey` — for a message on a row's thread. The request body is the raw file; its name and target field ride the query string. Files must be 25MB or smaller and of an allowed type (images, PDF, CSV, plain text, common Office documents) — anything else, including SVG, is refused outright rather than stored and relabelled later. The upload is STAGED: bind a cell file by PUTting the cell's complete membership to `/{tableId}/rows/{rowId}/attachments/{fieldKey}`, and a thread file by naming it in `attachmentIds` on `POST /{tableId}/rows/{rowId}/messages`. An upload never bound is destroyed automatically after 24 hours.

## Path parameters

- `tableId` string, required: Table ID

## Query parameters

- `fieldKey` string
- `filename` string, required
- `existingCount` integer, nullable

## Response `201`

File staged

- object
  - `success` true, required
  - `attachment` object, required
    - `id` string, required
    - `workspaceId` string, required
    - `tableId` string, required
    - `rowId` string
    - `fieldId` string, nullable, required
    - `messageId` string
    - `position` integer
    - `filename` string, required
    - `contentType` string, required
    - `sizeBytes` integer, required
    - `createdBy` union, required
      - object
        - `kind` 'user', required
        - `userId` string, required
      - object
        - `kind` 'sync', required
        - `feedId` string, required
        - `jobId` string
        - `cause` object
          - `automationId` string, required
          - `runId` string, required
          - `depth` integer, required
          - `untrustedSender` true
      - object
        - `kind` 'extractor', required
        - `feedId` string, required
        - `jobId` string
        - `untrustedSender` true
      - object
        - `kind` 'import', required
        - `importId` string, required
        - `userId` string, required
      - object
        - `kind` 'api', required
        - `clientId` string, required
      - object
        - `kind` 'system', required
        - `reason` string, required
      - object
        - `kind` 'automation', required
        - `automationId` string, required
        - `revision` string, required
        - `grantId` string, required
        - `runId` string, required
        - `node` string, required
        - `startedBy` ActorRef, required (recursive)
        - `depth` integer, required
        - `untrustedSender` true
    - `createdAt` string, required

## Other responses

- `400`: Validation failed, or a read-only field was written — `code` names the refusal and `details[]` the offending field or cell
- `401`: Unauthorized
- `403`: Forbidden — missing scope, insufficient role, or plan limit reached
- `404`: Not found
- `409`: The table is in the trash
- `413`: The file is larger than the per-file cap

---

[API](https://skmtc.dev/banksync/apis/banksync-api.md) · [All operations](https://skmtc.dev/banksync/apis/banksync-api/llms.txt) · [OpenAPI document](https://skmtc.dev/banksync/apis/banksync-api/revisions/94465f5ee922?raw)
