# Add or update rows

Source: https://neondeerdata.com/docs/api/lookup-tables/upsert-rows/

Match rows by a lookup key, update matches, and add missing rows.

## Request

```http
POST /v1/lookup-tables/{table_id}/rows/upsert

```

Adds or updates rows by a unique field: each item updates the row whose key_field holds its key, or adds a row. key_field must be a field with a lookup, which Add lookup makes. Each item succeeds or fails on its own and the answer is 200 even when some fail, so read every item's outcome. With all_or_nothing true, nothing is written if any item would fail: the answer is 422 batch_rejected, with every item's outcome in results. Needs an API key that can write. Values an update replaces are not kept, so it can't be undone.

Requires `lookup_tables:write`. The connection also needs access to the relevant tables, objects, or workflows.

MCP action: `upsert_lookup_rows`. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.

## Parameters

| Name                       | Send in                   | Required           | Type or allowed values                | Notes          |
| -------------------------- | ------------------------- | ------------------ | ------------------------------------- | -------------- |
| `table_id`                 | path                      | Yes                | string                                |                |
| `key_field`                | JSON body                 | Yes                | string                                |                |
| `items`                    | JSON body                 | Yes                | array of object                       |                |
| `items[].key`              | Within the JSON value     | Within this object | string or number or boolean or object |                |
| `items[].key.objectId`     | Within the JSON value     | Within this object | string (uuid)                         | Alternative 4. |
| `items[].key.recordId`     | Within the JSON value     | Within this object | string (uuid)                         | Alternative 4. |
| `items[].values`           | Within the JSON value     | Within this object | object                                |                |
| `items[].expected_version` | Within the JSON value     | No                 | integer                               |                |
| `all_or_nothing`           | JSON body                 | No                 | boolean                               |                |
| `request_key`              | Header: `Idempotency-Key` | Yes                | string                                |                |

## Response

| HTTP status | Result                                                                                 |
| ----------- | -------------------------------------------------------------------------------------- |
| `200`       | One result per item, in the order sent, and how many were created, updated and failed. |

Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.

| Response field                          | Type                  | Required in object | Notes                          |
| --------------------------------------- | --------------------- | ------------------ | ------------------------------ |
| `data`                                  | object                | Yes                |                                |
| `data.object`                           | `"lookup_row_upsert"` | Yes                |                                |
| `data.summary`                          | object                | Yes                |                                |
| `data.summary.created`                  | integer               | Yes                |                                |
| `data.summary.updated`                  | integer               | Yes                |                                |
| `data.summary.failed`                   | integer               | Yes                |                                |
| `data.results`                          | array of object       | Yes                |                                |
| `data.results[].index`                  | integer               | Yes                |                                |
| `data.results[].outcome`                | `"created"`           | Yes                | Alternative 1.                 |
| `data.results[].row`                    | object                | Yes                | Alternative 1\. Alternative 2. |
| `data.results[].row.id`                 | string (uuid)         | Yes                | Alternative 1\. Alternative 2. |
| `data.results[].row.object`             | `"lookup_row"`        | Yes                | Alternative 1\. Alternative 2. |
| `data.results[].row.values`             | object                | Yes                | Alternative 1\. Alternative 2. |
| `data.results[].row.version`            | integer               | Yes                | Alternative 1\. Alternative 2. |
| `data.results[].row.roster_verified_at` | string (date-time)    | No                 | Alternative 1\. Alternative 2. |
| `data.results[].outcome`                | `"updated"`           | Yes                | Alternative 2.                 |
| `data.results[].outcome`                | `"error"`             | Yes                | Alternative 3.                 |
| `data.results[].error`                  | object                | Yes                | Alternative 3.                 |
| `data.results[].error.code`             | string                | Yes                | Alternative 3.                 |
| `data.results[].error.message`          | string                | Yes                | Alternative 3.                 |
| `data.results[].error.param`            | string                | No                 | Alternative 3.                 |

## Errors

| HTTP status | Meaning                                                                                                                                                   |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`       | One of: invalid_request, invalid_filter, invalid_value, invalid_cursor, credential_in_url.                                                                |
| `401`       | One of: missing_credential, invalid_credential, credential_expired, credential_revoked.                                                                   |
| `402`       | One of: payment_overdue.                                                                                                                                  |
| `403`       | One of: plan_required, not_available, insufficient_scope, operation_not_allowed, field_not_writable, plan_capacity_exceeded.                              |
| `404`       | One of: resource_not_found.                                                                                                                               |
| `409`       | One of: table_filling, table_fill_failed, lookup_key_required, key_conflict, version_conflict, table_busy, table_capacity_exceeded, idempotency_conflict. |
| `413`       | One of: request_too_large.                                                                                                                                |
| `422`       | batch_rejected: all_or_nothing was set and an item would fail. Nothing was written; results gives each item's error, or not_applied.                      |
| `423`       | One of: organization_reopening.                                                                                                                           |
| `428`       | One of: customer_agreement_required.                                                                                                                      |
| `429`       | One of: rate_limited.                                                                                                                                     |
| `503`       | One of: service_unavailable.                                                                                                                              |

See [errors and recovery](https://neondeerdata.com/docs/api/errors/) and [safe retries](https://neondeerdata.com/docs/api/requests/) before repeating a write.

The [public OpenAPI document](https://neondeerdata.com/docs/api/meta/openapi/) lists generally available API families. Alpha operations may not appear in that document; their request and response fields are listed on this page. Use the [API Playground](https://neondeerdata.com/docs/api/testing/) to inspect a real response with your connection's access.
