curl --request POST \
--url https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"content": "<string>",
"matchColumns": [
"<string>"
],
"filename": "<string>",
"enrich": true,
"reenrichUpdated": false,
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file"
payload = {
"content": "<string>",
"matchColumns": ["<string>"],
"filename": "<string>",
"enrich": True,
"reenrichUpdated": False,
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
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({
content: '<string>',
matchColumns: ['<string>'],
filename: '<string>',
enrich: true,
reenrichUpdated: false,
batchId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file', 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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file",
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([
'content' => '<string>',
'matchColumns' => [
'<string>'
],
'filename' => '<string>',
'enrich' => true,
'reenrichUpdated' => false,
'batchId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file"
payload := strings.NewReader("{\n \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file")
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 \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"object": "enrichment_run",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tableRunId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"counts": {
"inserted": 123,
"updated": 123,
"skipped": 123
}
}{
"object": "enrichment_run",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tableRunId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"counts": {
"inserted": 123,
"updated": 123,
"skipped": 123
}
}Upsert rows from a CSV file (same operation, CSV transport)
The CSV transport for POST /tables/{tableId}/rows/upsert: content
is a base64-encoded CSV whose headers are input-column slugs (the same
keys as JSON upsert rows) with one record per data row. Identical
match/enrich semantics, identical per-batchId idempotency, identical
row cap: a CSV with more than 100 data rows is rejected with
413 TOO_MANY_ROWS (split the file or page the JSON upsert).
curl --request POST \
--url https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"content": "<string>",
"matchColumns": [
"<string>"
],
"filename": "<string>",
"enrich": true,
"reenrichUpdated": false,
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file"
payload = {
"content": "<string>",
"matchColumns": ["<string>"],
"filename": "<string>",
"enrich": True,
"reenrichUpdated": False,
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
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({
content: '<string>',
matchColumns: ['<string>'],
filename: '<string>',
enrich: true,
reenrichUpdated: false,
batchId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file', 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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file",
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([
'content' => '<string>',
'matchColumns' => [
'<string>'
],
'filename' => '<string>',
'enrich' => true,
'reenrichUpdated' => false,
'batchId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file"
payload := strings.NewReader("{\n \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\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://origami.chat/api/v2/tables/{tableId}/rows/upsert-file")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://origami.chat/api/v2/tables/{tableId}/rows/upsert-file")
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 \"content\": \"<string>\",\n \"matchColumns\": [\n \"<string>\"\n ],\n \"filename\": \"<string>\",\n \"enrich\": true,\n \"reenrichUpdated\": false,\n \"batchId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"object": "enrichment_run",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tableRunId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"counts": {
"inserted": 123,
"updated": 123,
"skipped": 123
}
}{
"object": "enrichment_run",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"batchId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tableRunId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"counts": {
"inserted": 123,
"updated": 123,
"skipped": 123
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
Transport-level replay protection: any v2 POST may send it
(1-255 printable ASCII characters; UUIDs recommended). The first
response for a key is recorded and replayed verbatim for retries
with the same key + method/path/body for 24 hours; replayed
responses carry the Idempotency-Replay: true response header.
Reusing a key with a different request, or retrying while the
original is still in flight, returns 409 IDEMPOTENCY_ERROR.
5xx responses are never recorded (the retry re-executes).
1 - 255^[\x21-\x7e]{1,255}$Path Parameters
Body
Response
Idempotent replay of an existing batchId (same receipt shape).
Response of POST /tables/{tableId}/rows/upsert and
.../rows/upsert-file: an enrichment_run reference the caller
polls via GET /enrichment-runs/{runId}.
"enrichment_run"Legacy alias of id.
Linked table-run parent (enrich=true batches only). Lets consumers
poll GET /tables/{tableId}/runs/{runId} or await the
table.run.completed webhook without a version bump.
null for enrich=false writes and pre-migration batches.
Completion is first-owner scoped: overlapping cells already active
under an earlier run are not adopted into this parent, so this run
can complete while those cells are still executing elsewhere.
Show child attributes
Show child attributes