---
title: "Upsert entity records"
method: POST
path: "/api/apps/{app_id}/entities/{entity_name}/upsert"
---

# Upsert entity records

`POST /api/apps/{app_id}/entities/{entity_name}/upsert`

## Request

- Base URL: `https://app.base44.com`
- URL: `https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert`
- Auth: HTTP bearer

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Creates or updates up to 500 records in one of the app's entities in one call, matching them to stored records by the fields you name in `key`.

For each record you send, a stored record with the same values in every `key` field is updated, and otherwise a new record is created. For example, with `"key": "order_number"`, re-sending an order updates it instead of adding a copy, so a sync job can send the same records again safely. An update merges like [Update entity record](/api-reference/update-entity-record), so a field you leave out keeps its value. Only the fields the entity's schema declares are stored, so a misspelled name is left out of the record instead of failing the call. Base44 always assigns `id`, `created_date`, `updated_date`, `created_by`, and `created_by_id` automatically, and ignores any of them you send.

`key` fields must be fields the entity's schema declares as a string, number, integer or boolean, and every record needs a value for each of them. Values are compared as the schema stores them, so `"42"` matches a stored `42` in a number field. When two records you send share a key, the later one wins. When several stored records share a key, the newest is updated.

Row-level security applies. Only stored records the entity's `rls` update rule lets you change are matched, so a record you can't change gets a new copy rather than an update, and new records must be covered by the `rls` create rule. Every record is checked before any is written, and one that fails rejects the call. If a call fails while it's writing, some records can already be stored, and sending it again finishes the job. Wait for the first call to finish before you retry: there's no `Idempotency-Key`, so two calls running at once can both create the same new record.

`records` in the response lists the created records first, then the updated ones. Like [Create entity records](/api-reference/create-entity-records), this doesn't trigger the app's webhooks, automations, or workflows.

<Note>This endpoint accepts a personal API key belonging to a user with access to the app. A read-only key is refused, and workspace API keys are not accepted.</Note>

## Path parameters

- `app_id` string, required: ID of the app that owns the entity.
- `entity_name` string, required: Name of the entity, exactly as [List entity schemas](/api-reference/list-entity-schemas) reports it. Don't pass `User` here. It doesn't fail, but it reads and writes a separate, disconnected set of records stored under that name, not the app's real user accounts, which are managed through their own endpoints.

## Request body

- UpsertPayload
  - `records` object[], required: The records to create or update, up to 500, each a flat JSON object of the fields the entity's schema declares. Every record needs a value for each `key` field.
  - `key` union, required: Field, or list of fields, that identifies a record. A stored record whose values in these fields match a record you send is updated, and otherwise a new record is created.
    - string
    - string[]

## Response `200`

How many records were created and updated, and the records themselves.

- object: How many records were created and updated, and the records themselves.
  - `created` integer, required: Number of new records created.
  - `updated` integer, required: Number of stored records updated.
  - `records` object[], required: The created records, then the updated ones.
    - `id` string, nullable: ID of the record. Pass it as `entity_id` to [Get entity record](/api-reference/get-entity-record), [Update entity record](/api-reference/update-entity-record) or [Delete entity record](/api-reference/delete-entity-record).
    - `created_date` string, nullable: When the record was created, as a UTC timestamp in ISO 8601 format. A record Base44 has just created carries a `Z` suffix, and a record read back from storage does not.
    - `updated_date` string, nullable: When the record last changed, as a UTC timestamp in ISO 8601 format. A record Base44 has just created carries a `Z` suffix, and a record read back from storage does not.
    - `created_by` string, nullable: Email of the app user who created the record, or `anonymous` when a visitor created it on an app that needs no login. Apps that hide record authorship leave this field out of the response.
    - `created_by_id` string, nullable: ID of the app user who created the record, or `anonymous` when a visitor created it on an app that needs no login.
    - `is_sample` boolean, nullable: Whether Base44 stored the record as sample data while the app was being built. A record you create reports `false`.

## Other responses

- `400`: `records` is empty or holds more than 500 records, a `key` field isn't a string, number, integer or boolean field the entity's schema declares, a record has no value for a `key` field or one that doesn't fit its type, a value is over 20,000 characters on an app that limits field size, or the entity is `User`.
- `401`: Missing or invalid credentials.
- `403`: You don't have access to this app, the entity's `rls` create rule or a field-level rule doesn't cover one of the records, or your API key is read-only.
- `404`: App not found, or the app has no entity with this name.
- `422`: `records` or `key` is missing, `records` isn't an array of JSON objects, or a new record is missing a required field the entity's schema declares or has a value that doesn't match its type.
- `429`: Rate limit exceeded. The base limit is 25 requests per minute, and this endpoint shares it with [Create entity records](/api-reference/create-entity-records). See [Rate limits](/developers/references/apps-api/get-started/rate-limits) for the multiplier your plan gets.

## Changes

- **2026-10-11** `80287813ab84`: 1 info
  - the endpoint scheme security `PersonalAccessTokenAuth` was added to the API
- **2026-10-10** `fc604cdcc3a2`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/idealspot/apis/base44-app-management-api/changes/api/apps/:app_id/entities/:entity_name/upsert/post.md)

---

[API](https://skmtc.dev/idealspot/apis/base44-app-management-api.md) - [All operations](https://skmtc.dev/idealspot/apis/base44-app-management-api/llms.txt) - [OpenAPI document](https://skmtc.dev/idealspot/apis/base44-app-management-api/revisions/b5b4ebd82a1e?raw)
