---
title: "Update table row cells"
method: PATCH
path: "/v1/tables/{tableId}/rows/{rowId}"
tags: ["Tables"]
---

# Update table row cells

`PATCH /v1/tables/{tableId}/rows/{rowId}`

## Request

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

A FIELD-SCOPED patch: each entry writes one cell, so two callers editing different cells of one row never clobber each other. Row identity is recomputed over the merged row, so a partial edit cannot give the row a new, wrong identity. `null` clears a cell. `expectedVersion` is the row version you read, judged PER CELL: the patch lands unless a cell it writes was changed by another edit after that version, in which case it is a 409 `ROW_CELL_CONFLICT` whose `details` name each such cell with its current value (`{ theirs, theirVersion }` as JSON) and nothing is written. A change to some other cell of the row is not a conflict, and a retried patch that finds its own value already in the cell is not one either. A version newer than the row is a 409 `ROW_VERSION_CONFLICT`. Omitting `expectedVersion` overwrites whatever the cells hold.

## Path parameters

- `tableId` string, required: Table ID
- `rowId` string, required: Row ID

## Request body

- object
  - `patches` object[], required
    - `fieldKey` string, required
    - `value` unknown
  - `expectedVersion` integer

## Response `200`

Row updated

- object
  - `success` true, required
  - `data` object, required
    - `id` string, required
    - `tableId` string, required
    - `version` integer, required
    - `properties` object, required
    - `provenance` union, required
      - object
        - `kind` 'sync', required
        - `feedId` string, required
        - `bankId` string, required
        - `accountId` string
        - `jobId` string
        - `bankWorkspaceId` string
      - object
        - `kind` 'extractor', required
        - `feedId` string, required
        - `jobId` string
      - object
        - `kind` 'user', required
        - `userId` string, required
      - object
        - `kind` 'import', required
        - `importId` string, required
        - `userId` string, required
      - object
        - `kind` 'api', required
        - `clientId` string, required
    - `createdAt` string, required
    - `updatedAt` string, required
    - `deletedAt` string

## 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`: Conflict — the table is locked, the schema or row version moved, a cell the patch writes was changed by another edit (`ROW_CELL_CONFLICT`, with each cell’s current value in `details`), or a relation accepts one row. `code` says which.

---

[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)
