curl --request POST \
--url https://api.occtoo.com/sources/{sourceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"entries": [
{
"id": "sku-123",
"properties": [
{
"id": "tags",
"value": [
"summer",
"sale"
]
},
{
"id": "price",
"value": 100.111
},
{
"id": "publishedAt",
"value": "2026-01-01T00:00:00Z"
},
{
"id": "inStock",
"value": true
},
{
"id": "name",
"value": "Blue chair",
"language": "en"
}
]
}
]
}
'import requests
url = "https://api.occtoo.com/sources/{sourceId}"
payload = { "entries": [
{
"id": "sku-123",
"properties": [
{
"id": "tags",
"value": ["summer", "sale"]
},
{
"id": "price",
"value": 100.111
},
{
"id": "publishedAt",
"value": "2026-01-01T00:00:00Z"
},
{
"id": "inStock",
"value": True
},
{
"id": "name",
"value": "Blue chair",
"language": "en"
}
]
}
] }
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({
entries: [
{
id: 'sku-123',
properties: [
{id: 'tags', value: ['summer', 'sale']},
{id: 'price', value: 100.111},
{id: 'publishedAt', value: '2026-01-01T00:00:00Z'},
{id: 'inStock', value: true},
{id: 'name', value: 'Blue chair', language: 'en'}
]
}
]
})
};
fetch('https://api.occtoo.com/sources/{sourceId}', 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://api.occtoo.com/sources/{sourceId}",
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([
'entries' => [
[
'id' => 'sku-123',
'properties' => [
[
'id' => 'tags',
'value' => [
'summer',
'sale'
]
],
[
'id' => 'price',
'value' => 100.111
],
[
'id' => 'publishedAt',
'value' => '2026-01-01T00:00:00Z'
],
[
'id' => 'inStock',
'value' => true
],
[
'id' => 'name',
'value' => 'Blue chair',
'language' => 'en'
]
]
]
]
]),
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://api.occtoo.com/sources/{sourceId}"
payload := strings.NewReader("{\n \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\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://api.occtoo.com/sources/{sourceId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.occtoo.com/sources/{sourceId}")
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 \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"correlationId": "83b538a7-df7c-4cf4-b988-cd3b71c4cd90",
"sourceId": "products",
"acceptedAt": "2026-08-13T10:15:30Z",
"acceptedEntryCount": 1,
"newPropertiesFound": [
{
"id": "tags",
"type": "List",
"delimiter": ","
}
]
}{
"errors": {},
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}Ingest typed JSON into a source
Validates typed JSON values against the source property configuration and accepts the entries for asynchronous processing.
Values retain their JSON types at the API boundary. They are serialized to the source’s string-at-rest representation before queueing. Properties without a configured type are inferred from JSON; list delimiters default to , only when no delimiter is already configured.
A successful 202 response returns the ingest batch correlation id, accepted source and entry count, UTC acceptance time, and any previously absent properties inferred from this request. Acceptance means the batch was queued; downstream entry processing and worker-owned property registration may still be running.
curl --request POST \
--url https://api.occtoo.com/sources/{sourceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"entries": [
{
"id": "sku-123",
"properties": [
{
"id": "tags",
"value": [
"summer",
"sale"
]
},
{
"id": "price",
"value": 100.111
},
{
"id": "publishedAt",
"value": "2026-01-01T00:00:00Z"
},
{
"id": "inStock",
"value": true
},
{
"id": "name",
"value": "Blue chair",
"language": "en"
}
]
}
]
}
'import requests
url = "https://api.occtoo.com/sources/{sourceId}"
payload = { "entries": [
{
"id": "sku-123",
"properties": [
{
"id": "tags",
"value": ["summer", "sale"]
},
{
"id": "price",
"value": 100.111
},
{
"id": "publishedAt",
"value": "2026-01-01T00:00:00Z"
},
{
"id": "inStock",
"value": True
},
{
"id": "name",
"value": "Blue chair",
"language": "en"
}
]
}
] }
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({
entries: [
{
id: 'sku-123',
properties: [
{id: 'tags', value: ['summer', 'sale']},
{id: 'price', value: 100.111},
{id: 'publishedAt', value: '2026-01-01T00:00:00Z'},
{id: 'inStock', value: true},
{id: 'name', value: 'Blue chair', language: 'en'}
]
}
]
})
};
fetch('https://api.occtoo.com/sources/{sourceId}', 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://api.occtoo.com/sources/{sourceId}",
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([
'entries' => [
[
'id' => 'sku-123',
'properties' => [
[
'id' => 'tags',
'value' => [
'summer',
'sale'
]
],
[
'id' => 'price',
'value' => 100.111
],
[
'id' => 'publishedAt',
'value' => '2026-01-01T00:00:00Z'
],
[
'id' => 'inStock',
'value' => true
],
[
'id' => 'name',
'value' => 'Blue chair',
'language' => 'en'
]
]
]
]
]),
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://api.occtoo.com/sources/{sourceId}"
payload := strings.NewReader("{\n \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\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://api.occtoo.com/sources/{sourceId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.occtoo.com/sources/{sourceId}")
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 \"entries\": [\n {\n \"id\": \"sku-123\",\n \"properties\": [\n {\n \"id\": \"tags\",\n \"value\": [\n \"summer\",\n \"sale\"\n ]\n },\n {\n \"id\": \"price\",\n \"value\": 100.111\n },\n {\n \"id\": \"publishedAt\",\n \"value\": \"2026-01-01T00:00:00Z\"\n },\n {\n \"id\": \"inStock\",\n \"value\": true\n },\n {\n \"id\": \"name\",\n \"value\": \"Blue chair\",\n \"language\": \"en\"\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"correlationId": "83b538a7-df7c-4cf4-b988-cd3b71c4cd90",
"sourceId": "products",
"acceptedAt": "2026-08-13T10:15:30Z",
"acceptedEntryCount": 1,
"newPropertiesFound": [
{
"id": "tags",
"type": "List",
"delimiter": ","
}
]
}{
"errors": {},
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"traceId": "<string>"
}Before you begin
You need the customer-facing source ID and a tenant-level Application bearer token with thewrite:sources scope. The Application must be granted access to the target source or to all sources. See Application access tokens to request a token for the tenant audience.
import-datasource permission. This endpoint requires write:sources, so use a tenant-level Application token.Typed ingest behavior
Every entry is an upsert. This endpoint does not accept a delete flag. Values keep their native JSON types at the API boundary and are serialized before they enter the asynchronous ingest pipeline.| Source property type | Accepted JSON value |
|---|---|
Text | String |
LocalizedText | String with language |
List | Array of strings |
LocalizedList | Array of strings with language |
Boolean | Boolean |
Timestamp | ISO 8601 string |
Integer | Whole JSON number within the 64-bit integer range |
Decimal | Finite JSON number |
language. Non-localized properties reject it. A null value clears a configured property by storing an empty value.
Timestamp. An unconfigured string is inferred as Text or LocalizedText.Type inference for new properties
When a property has no configured type, Occtoo infers it from all values for that property in the request. Strings becomeText or LocalizedText; string arrays become List or LocalizedList; booleans become Boolean; whole numbers become Integer; and fractional numbers become Decimal. Integer and decimal values in one request are promoted to Decimal. Other incompatible shapes reject the complete request. Occtoo cannot infer a property from only null values.
New list properties use , as their delimiter. Existing list properties keep their configured delimiter, and list elements cannot contain it. Property IDs are matched case-insensitively and stored in lowercase. newPropertiesFound reports inferred properties, but the ingestion worker registers them asynchronously, so they may not appear in Configuration immediately.
Processing and errors
Validation is all-or-nothing. A400 response returns validation messages keyed by request path. A 202 response means the complete batch passed validation and was queued; downstream entry processing and property registration may still be running. Keep correlationId for diagnostics, and do not retry a successful request because data is not visible immediately. Retry 429 and transient 5xx responses with bounded exponential backoff, respecting Retry-After when present.Authorizations
Sign in with the existing Occtoo Studio identity and selected organization.
Path Parameters
Stable customer-facing source id within the authenticated tenant.
1"products"
Body
Non-empty collection of source entries.
1Show child attributes
Show child attributes
Response
The typed entries were validated, serialized, and queued for asynchronous processing.
Correlation id assigned to this asynchronous ingest batch. This is distinct from the HTTP trace id.
"83b538a7-df7c-4cf4-b988-cd3b71c4cd90"
Customer-facing id of the source that accepted the entries.
"products"
UTC time at which the ingest batch was accepted and queued.
"2026-08-13T10:15:30Z"
Number of entries accepted in this all-or-nothing request.
x >= 11
Properties absent from the loaded source configuration and inferred by this request. The ingestion worker registers them asynchronously; they may not be visible in Configuration immediately. Empty when no new properties were found.
Show child attributes
Show child attributes
Was this page helpful?