curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"records": [
{
"amount": 4200,
"order_number": "A-1001",
"status": "paid"
},
{
"amount": 1800,
"order_number": "A-1002",
"status": "draft"
}
],
"key": "order_number"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert"
payload = {
"records": [
{
"amount": 4200,
"order_number": "A-1001",
"status": "paid"
},
{
"amount": 1800,
"order_number": "A-1002",
"status": "draft"
}
],
"key": "order_number"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
records: [
{amount: 4200, order_number: 'A-1001', status: 'paid'},
{amount: 1800, order_number: 'A-1002', status: 'draft'}
],
key: 'order_number'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'records' => [
[
'amount' => 4200,
'order_number' => 'A-1001',
'status' => 'paid'
],
[
'amount' => 1800,
'order_number' => 'A-1002',
'status' => 'draft'
]
],
'key' => 'order_number'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert"
payload := strings.NewReader("{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}"
response = http.request(request)
puts response.read_body{
"created": 1,
"updated": 1,
"records": [
{
"id": "6886b8d390dc7e2f4a2c91b4",
"created_date": "2026-06-05T08:12:44.902000Z",
"updated_date": "2026-06-05T08:12:44.902000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1002",
"status": "draft",
"amount": 1800
},
{
"id": "6886b8d390dc7e2f4a2c91b3",
"created_date": "2026-06-01T09:23:41.481000Z",
"updated_date": "2026-06-05T08:12:44.915000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1001",
"status": "paid",
"amount": 4200
}
]
}Upsert entity records
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, 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, this doesn’t trigger the app’s webhooks, automations, or workflows.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"records": [
{
"amount": 4200,
"order_number": "A-1001",
"status": "paid"
},
{
"amount": 1800,
"order_number": "A-1002",
"status": "draft"
}
],
"key": "order_number"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert"
payload = {
"records": [
{
"amount": 4200,
"order_number": "A-1001",
"status": "paid"
},
{
"amount": 1800,
"order_number": "A-1002",
"status": "draft"
}
],
"key": "order_number"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
records: [
{amount: 4200, order_number: 'A-1001', status: 'paid'},
{amount: 1800, order_number: 'A-1002', status: 'draft'}
],
key: 'order_number'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'records' => [
[
'amount' => 4200,
'order_number' => 'A-1001',
'status' => 'paid'
],
[
'amount' => 1800,
'order_number' => 'A-1002',
'status' => 'draft'
]
],
'key' => 'order_number'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert"
payload := strings.NewReader("{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/upsert")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"records\": [\n {\n \"amount\": 4200,\n \"order_number\": \"A-1001\",\n \"status\": \"paid\"\n },\n {\n \"amount\": 1800,\n \"order_number\": \"A-1002\",\n \"status\": \"draft\"\n }\n ],\n \"key\": \"order_number\"\n}"
response = http.request(request)
puts response.read_body{
"created": 1,
"updated": 1,
"records": [
{
"id": "6886b8d390dc7e2f4a2c91b4",
"created_date": "2026-06-05T08:12:44.902000Z",
"updated_date": "2026-06-05T08:12:44.902000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1002",
"status": "draft",
"amount": 1800
},
{
"id": "6886b8d390dc7e2f4a2c91b3",
"created_date": "2026-06-01T09:23:41.481000Z",
"updated_date": "2026-06-05T08:12:44.915000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1001",
"status": "paid",
"amount": 4200
}
]
}Authorizations
Personal access token, sent as Authorization: Bearer <token>.
Path Parameters
ID of the app that owns the entity.
Name of the entity, exactly as 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.
Body
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.
[
{
"amount": 4200,
"order_number": "A-1001",
"status": "paid"
},
{
"amount": 1800,
"order_number": "A-1002",
"status": "draft"
}
]
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.
"order_number"
Response
How many records were created and updated, and the records themselves.
How many records were created and updated, and the records themselves.
Number of new records created.
1
Number of stored records updated.
1
The created records, then the updated ones.
Show child attributes
Show child attributes
[
{
"id": "6886b8d390dc7e2f4a2c91b4",
"created_date": "2026-06-05T08:12:44.902000Z",
"updated_date": "2026-06-05T08:12:44.902000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1002",
"status": "draft",
"amount": 1800
},
{
"id": "6886b8d390dc7e2f4a2c91b3",
"created_date": "2026-06-01T09:23:41.481000Z",
"updated_date": "2026-06-05T08:12:44.915000Z",
"created_by": "jane@acme.com",
"created_by_id": "6874b0c2e1a94d0031bb77de",
"is_sample": false,
"order_number": "A-1001",
"status": "paid",
"amount": 4200
}
]
Was this page helpful?