curl --request POST \
--url https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"address_line_1": "1250 Main St",
"allow_create": true,
"city": "Springfield",
"country": "US",
"country_derived": true,
"extras": {
"branch_code": "595",
"opening_hours_spec": [
{
"closes": "20:00",
"dayOfWeek": "Monday",
"opens": "10:00"
}
],
"services": [
"Salon",
"Brow Bar"
]
},
"first_party": true,
"lat": 39.7817,
"lon": -89.6501,
"merged_extras": {
"osm": {
"licence": "ODbL",
"osm_id": "node/123456789",
"tags": {
"shop": "cosmetics"
}
}
},
"name": "Ulta Beauty",
"opening_hours": "Mo-Sa 10:00-20:00; Su 11:00-18:00",
"phone": "+12175550100",
"postal_code": "62704",
"region": "IL",
"source": "ulta_site",
"source_url": "https://www.ulta.com/stores/springfield-il-595",
"store_number": "595",
"website": "https://www.ulta.com/stores/springfield-il-595"
}
'import requests
url = "https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores"
payload = {
"address_line_1": "1250 Main St",
"allow_create": True,
"city": "Springfield",
"country": "US",
"country_derived": True,
"extras": {
"branch_code": "595",
"opening_hours_spec": [
{
"closes": "20:00",
"dayOfWeek": "Monday",
"opens": "10:00"
}
],
"services": ["Salon", "Brow Bar"]
},
"first_party": True,
"lat": 39.7817,
"lon": -89.6501,
"merged_extras": { "osm": {
"licence": "ODbL",
"osm_id": "node/123456789",
"tags": { "shop": "cosmetics" }
} },
"name": "Ulta Beauty",
"opening_hours": "Mo-Sa 10:00-20:00; Su 11:00-18:00",
"phone": "+12175550100",
"postal_code": "62704",
"region": "IL",
"source": "ulta_site",
"source_url": "https://www.ulta.com/stores/springfield-il-595",
"store_number": "595",
"website": "https://www.ulta.com/stores/springfield-il-595"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
address_line_1: '1250 Main St',
allow_create: true,
city: 'Springfield',
country: 'US',
country_derived: true,
extras: {
branch_code: '595',
opening_hours_spec: [{closes: '20:00', dayOfWeek: 'Monday', opens: '10:00'}],
services: ['Salon', 'Brow Bar']
},
first_party: true,
lat: 39.7817,
lon: -89.6501,
merged_extras: {osm: {licence: 'ODbL', osm_id: 'node/123456789', tags: {shop: 'cosmetics'}}},
name: 'Ulta Beauty',
opening_hours: 'Mo-Sa 10:00-20:00; Su 11:00-18:00',
phone: '+12175550100',
postal_code: '62704',
region: 'IL',
source: 'ulta_site',
source_url: 'https://www.ulta.com/stores/springfield-il-595',
store_number: '595',
website: 'https://www.ulta.com/stores/springfield-il-595'
})
};
fetch('https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores', 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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores",
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([
'address_line_1' => '1250 Main St',
'allow_create' => true,
'city' => 'Springfield',
'country' => 'US',
'country_derived' => true,
'extras' => [
'branch_code' => '595',
'opening_hours_spec' => [
[
'closes' => '20:00',
'dayOfWeek' => 'Monday',
'opens' => '10:00'
]
],
'services' => [
'Salon',
'Brow Bar'
]
],
'first_party' => true,
'lat' => 39.7817,
'lon' => -89.6501,
'merged_extras' => [
'osm' => [
'licence' => 'ODbL',
'osm_id' => 'node/123456789',
'tags' => [
'shop' => 'cosmetics'
]
]
],
'name' => 'Ulta Beauty',
'opening_hours' => 'Mo-Sa 10:00-20:00; Su 11:00-18:00',
'phone' => '+12175550100',
'postal_code' => '62704',
'region' => 'IL',
'source' => 'ulta_site',
'source_url' => 'https://www.ulta.com/stores/springfield-il-595',
'store_number' => '595',
'website' => 'https://www.ulta.com/stores/springfield-il-595'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores"
payload := strings.NewReader("{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}"
response = http.request(request)
puts response.read_body{
"already_claimed_stores": 0,
"committed": false,
"coverage_after": {
"stores": 1622,
"with_phone": 3,
"with_point": 1622
},
"created": 1,
"enriched": 2,
"errors": 0,
"existing_stores": 1621,
"fields_superseded": 4,
"legacy_keys_preserved": 2,
"matched_address": 0,
"matched_proximity": 2,
"matched_store_number": 0,
"near_misses": 1,
"not_created": 0,
"phones_written": 3,
"rekey_skipped_conflict": 0,
"row_errors": [],
"rows": [
{
"conflict": "",
"distance_m": 18.4,
"error": "",
"index": 0,
"match_method": "proximity",
"outcome": "enriched",
"store_id": "mhvXdrZT4jP5T8vBxuvm75",
"store_number": "595"
}
],
"submitted_rows": 3,
"unchanged": 0,
"unmatched_existing": 1619,
"unmatched_existing_store_ids": [
"kX4bR8WqkQx2Y7jJ9mZt3P"
],
"urls_written": 3
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}Upsert one physical store
Reconcile and apply a single store row for one retailer. Identical to the batch endpoint with one row — the response is the same envelope, with a single entry in rows — so a one-store correction needs no list wrapping.
Rows are reconciled, not inserted. Each row is tied to an existing store by exact store number, then by proximity within proximity_m, then by normalised street plus postal code — one-to-one, highest-confidence tier first. A match enriches the existing store in place; only a row that matches nothing creates one. This is why the endpoint is safe to point at a full dataset that overlaps rows already in the database: an insert-only surface would plant a duplicate beside every store whose recorded key predates the retailer’s own numbering.
Nothing is ever merged, deactivated or deleted here. An existing store no row claims is counted in unmatched_existing and left exactly as it is; a pair the matcher finds contradictory is reported in the row’s conflict and left unrekeyed.
dry_run defaults to true. Send dry_run: false to commit.
retailer_id is a retailers.Retailer. An organization-private retailer requires OWNER or MANAGER on the organization that owns it; a canonical (Closient-curated) retailer requires a staff account, because those rows were previously writable only from a management command. The organization this credential acts for becomes the owning organization of any location record created.
curl --request POST \
--url https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"address_line_1": "1250 Main St",
"allow_create": true,
"city": "Springfield",
"country": "US",
"country_derived": true,
"extras": {
"branch_code": "595",
"opening_hours_spec": [
{
"closes": "20:00",
"dayOfWeek": "Monday",
"opens": "10:00"
}
],
"services": [
"Salon",
"Brow Bar"
]
},
"first_party": true,
"lat": 39.7817,
"lon": -89.6501,
"merged_extras": {
"osm": {
"licence": "ODbL",
"osm_id": "node/123456789",
"tags": {
"shop": "cosmetics"
}
}
},
"name": "Ulta Beauty",
"opening_hours": "Mo-Sa 10:00-20:00; Su 11:00-18:00",
"phone": "+12175550100",
"postal_code": "62704",
"region": "IL",
"source": "ulta_site",
"source_url": "https://www.ulta.com/stores/springfield-il-595",
"store_number": "595",
"website": "https://www.ulta.com/stores/springfield-il-595"
}
'import requests
url = "https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores"
payload = {
"address_line_1": "1250 Main St",
"allow_create": True,
"city": "Springfield",
"country": "US",
"country_derived": True,
"extras": {
"branch_code": "595",
"opening_hours_spec": [
{
"closes": "20:00",
"dayOfWeek": "Monday",
"opens": "10:00"
}
],
"services": ["Salon", "Brow Bar"]
},
"first_party": True,
"lat": 39.7817,
"lon": -89.6501,
"merged_extras": { "osm": {
"licence": "ODbL",
"osm_id": "node/123456789",
"tags": { "shop": "cosmetics" }
} },
"name": "Ulta Beauty",
"opening_hours": "Mo-Sa 10:00-20:00; Su 11:00-18:00",
"phone": "+12175550100",
"postal_code": "62704",
"region": "IL",
"source": "ulta_site",
"source_url": "https://www.ulta.com/stores/springfield-il-595",
"store_number": "595",
"website": "https://www.ulta.com/stores/springfield-il-595"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
address_line_1: '1250 Main St',
allow_create: true,
city: 'Springfield',
country: 'US',
country_derived: true,
extras: {
branch_code: '595',
opening_hours_spec: [{closes: '20:00', dayOfWeek: 'Monday', opens: '10:00'}],
services: ['Salon', 'Brow Bar']
},
first_party: true,
lat: 39.7817,
lon: -89.6501,
merged_extras: {osm: {licence: 'ODbL', osm_id: 'node/123456789', tags: {shop: 'cosmetics'}}},
name: 'Ulta Beauty',
opening_hours: 'Mo-Sa 10:00-20:00; Su 11:00-18:00',
phone: '+12175550100',
postal_code: '62704',
region: 'IL',
source: 'ulta_site',
source_url: 'https://www.ulta.com/stores/springfield-il-595',
store_number: '595',
website: 'https://www.ulta.com/stores/springfield-il-595'
})
};
fetch('https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores', 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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores",
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([
'address_line_1' => '1250 Main St',
'allow_create' => true,
'city' => 'Springfield',
'country' => 'US',
'country_derived' => true,
'extras' => [
'branch_code' => '595',
'opening_hours_spec' => [
[
'closes' => '20:00',
'dayOfWeek' => 'Monday',
'opens' => '10:00'
]
],
'services' => [
'Salon',
'Brow Bar'
]
],
'first_party' => true,
'lat' => 39.7817,
'lon' => -89.6501,
'merged_extras' => [
'osm' => [
'licence' => 'ODbL',
'osm_id' => 'node/123456789',
'tags' => [
'shop' => 'cosmetics'
]
]
],
'name' => 'Ulta Beauty',
'opening_hours' => 'Mo-Sa 10:00-20:00; Su 11:00-18:00',
'phone' => '+12175550100',
'postal_code' => '62704',
'region' => 'IL',
'source' => 'ulta_site',
'source_url' => 'https://www.ulta.com/stores/springfield-il-595',
'store_number' => '595',
'website' => 'https://www.ulta.com/stores/springfield-il-595'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores"
payload := strings.NewReader("{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/retailers/api/v1/retailers/{retailer_id}/stores")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"address_line_1\": \"1250 Main St\",\n \"allow_create\": true,\n \"city\": \"Springfield\",\n \"country\": \"US\",\n \"country_derived\": true,\n \"extras\": {\n \"branch_code\": \"595\",\n \"opening_hours_spec\": [\n {\n \"closes\": \"20:00\",\n \"dayOfWeek\": \"Monday\",\n \"opens\": \"10:00\"\n }\n ],\n \"services\": [\n \"Salon\",\n \"Brow Bar\"\n ]\n },\n \"first_party\": true,\n \"lat\": 39.7817,\n \"lon\": -89.6501,\n \"merged_extras\": {\n \"osm\": {\n \"licence\": \"ODbL\",\n \"osm_id\": \"node/123456789\",\n \"tags\": {\n \"shop\": \"cosmetics\"\n }\n }\n },\n \"name\": \"Ulta Beauty\",\n \"opening_hours\": \"Mo-Sa 10:00-20:00; Su 11:00-18:00\",\n \"phone\": \"+12175550100\",\n \"postal_code\": \"62704\",\n \"region\": \"IL\",\n \"source\": \"ulta_site\",\n \"source_url\": \"https://www.ulta.com/stores/springfield-il-595\",\n \"store_number\": \"595\",\n \"website\": \"https://www.ulta.com/stores/springfield-il-595\"\n}"
response = http.request(request)
puts response.read_body{
"already_claimed_stores": 0,
"committed": false,
"coverage_after": {
"stores": 1622,
"with_phone": 3,
"with_point": 1622
},
"created": 1,
"enriched": 2,
"errors": 0,
"existing_stores": 1621,
"fields_superseded": 4,
"legacy_keys_preserved": 2,
"matched_address": 0,
"matched_proximity": 2,
"matched_store_number": 0,
"near_misses": 1,
"not_created": 0,
"phones_written": 3,
"rekey_skipped_conflict": 0,
"row_errors": [],
"rows": [
{
"conflict": "",
"distance_m": 18.4,
"error": "",
"index": 0,
"match_method": "proximity",
"outcome": "enriched",
"store_id": "mhvXdrZT4jP5T8vBxuvm75",
"store_number": "595"
}
],
"submitted_rows": 3,
"unchanged": 0,
"unmatched_existing": 1619,
"unmatched_existing_store_ids": [
"kX4bR8WqkQx2Y7jJ9mZt3P"
],
"urls_written": 3
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}Authorizations
Path Parameters
Unique identifier of the retailer this store belongs to.
22^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$Body
A single store plus the same reconciliation knobs the batch form takes.
The key this source uses for the store — the retailer's own store number where it publishes one, otherwise a stable synthesized key (e.g. SYN-…). This is the highest-confidence match tier, so a source that can supply a real store number should: it is what makes a repeated call idempotent rather than proximity-matched.
50Stable name of the dataset this row came from (e.g. ulta_site, sephora_csv, osm). Load-bearing, not a label: per-column authorship is recorded against it, and a source may later correct a value it wrote itself but never one another source wrote. Sending a different name on a rerun of the same dataset therefore forfeits that source's own right to update the columns it authored.
1 - 100Store or branch display name.
255Street address, number and street name.
255City, town or locality.
255State, province or region.
255ZIP, postal code or postcode.
20ISO 3166-1 alpha-2 country code for the store. An assigned code: this value is written to the place's country column, and a store filed under a code that names no country is the same class of defect C-5969 found 75 instances of. A bare two-letter string would have admitted 427 unassigned combinations (C-5975).
2^[A-Za-z]{2}$WGS84 latitude in decimal degrees. Null is accepted — a store with a good address and no coordinates still matches on the address tier and is still worth having.
-90 <= x <= 90WGS84 longitude in decimal degrees. Null is accepted; see lat.
-180 <= x <= 180Store phone number; normalised to E.164 on write.
50The store's own page on the retailer's site.
1000Opening hours in OSM opening_hours syntax (e.g. Mo-Sa 10:00-20:00; Su 11:00-18:00). There is no hours column on a location, so this is stored in Place.source_extras under this row's source — sending it here is equivalent to sending extras.opening_hours, and an explicit extras.opening_hours wins. A structured schema.org openingHoursSpecification can be sent alongside it in extras.
1000Where this specific row was read from.
1000True when the row comes from the retailer's own site or feed. Only a first-party row may overwrite an existing non-placeholder value or rename a storefront; community mapping data enriches blanks instead.
Whether a row that matches nothing may create a new store. Set false for a secondary source: it may enrich a store the retailer confirms exists, but a location only a community extract believes in is not evidence enough to seed a storefront customers would be sent to.
True when country came from a coordinate lookup rather than from a feed label or address field. This is a provenance assertion, and it grants the row power: only a derived country may correct a country already stored on the place. A country that came from a label can fill a blank and nothing more.
Earn it by calling GET /locations/api/v1/countries/resolve?lat=&lon= with the row's own coordinates and sending the returned country as country: that is the same rule the server applies, so you need no polygon dataset of your own. Set it only when the answer is not flagged and method is not none. The reason it exists is concrete — Sephora's North America feed labels 482 of its 483 rows US, and trusting that label would have filed 75 Canadian stores as American (C-5969). A label is not evidence; a coordinate is. Asserting this on a label-derived country re-opens exactly that defect, so leave it false when in doubt: the cost is a country that stays wrong until a better row arrives, not one that gets overwritten with a worse value.
Everything this source carries that has no column of its own — raw hours specifications, service lists, OSM tags, source-conflict flags. Stored in Place.source_extras under this row's source. Nesting is allowed here, unlike the flat-string metadata field on other resources. A secondary source's payload does not belong here — send it in merged_extras under that source's name.
Provenance from other sources whose data was folded into this row before upload, keyed by each source's stable name — the same kind of name source takes. Use it when your importer merged a secondary dataset (e.g. an OSM extract) into a primary one: put the primary's payload in extras and each secondary's payload here, e.g. {"osm": {"licence": "ODbL", "tags": {...}}}. Each key is stored in Place.source_extras as its own sibling of source, so the sources stay separable and a later run of either one replaces only its own entry. Do not invent a convention such as an extras["merged:osm"] key: that buries the secondary under the primary's namespace where no other reader will look for it. A key equal to this row's source is rejected, because it would overwrite the row's own extras. Only the payload is recorded — per-column authorship still goes to source alone.
Show child attributes
Show child attributes
When true (the default) the write is rolled back. Send false to commit.
Match radius in metres for the proximity tier.
x <= 500ISO 3166-1 alpha-2 region for phone normalisation.
2^[A-Za-z]{2}$Response
OK
Batch census plus the per-row results.
created + enriched + unchanged + not_created + errors equals the
number of submitted rows for any 200 response. The endpoint returns 200
even when individual rows fail: a per-row failure is reported here rather
than aborting the batch, so a single malformed row cannot cost the other
999. Request-level failures (auth, an unwritable retailer, a batch over
the row cap) are 4xx instead.
False when this was a dry run — every path ran and the transaction was rolled back.
Number of rows in the request.
x >= 0Stores this retailer had when this call started, including any in already_claimed_store_ids. Measured per call: on a committed split run it grows by the stores earlier batches created.
x >= 0Rows that produced a new store.
x >= 0Rows that matched and changed something.
x >= 0Rows that matched and needed no change.
x >= 0Rows that matched nothing and were not allowed to create a store.
x >= 0Rows that raised. Equals the length of row_errors.
x >= 0Rows matched on the exact store-number tier.
x >= 0Rows matched on the proximity tier.
x >= 0Rows matched on the normalised-address tier.
x >= 0Pairs beyond proximity_m but within 500 m that were deliberately NOT matched. A judgement call for a human, never auto-matched by widening the radius. Per call: a split run's sum can include a pair whose store a later batch went on to claim.
x >= 0Existing stores no submitted row claimed and not in already_claimed_store_ids. For a full-dataset run these are stores the retailer's own list no longer carries, i.e. probably closed — reported, never deactivated or deleted. Per call, measured against the whole retailer: for a split run, read it from the last call, sent with every other batch's store_ids; summing per-call values double-counts.
x >= 0How many already_claimed_store_ids were this retailer's stores and so were held out of matching. Lower than the number sent means some ids were ignored — for example created ids from a dry run, whose stores never persisted.
x >= 0Phone numbers written or refreshed.
x >= 0Store URLs written or refreshed.
x >= 0Existing non-placeholder values a first-party row overwrote. The previous value is preserved under Place.source_extras['superseded'] rather than discarded.
x >= 0Stores whose pre-existing store number was replaced by this source's real one, with the old key kept in Place.source_extras['legacy_store_key'].
x >= 0Stores left on their existing key because the evidence for re-keying was contradictory.
x >= 0The ids of the stores counted in unmatched_existing, so a human can review exactly which stores the dataset no longer carries.
URL-safe 22-character shortuuid encoding of the row's UUID primary key. Stable across the row's lifetime; suitable for sharing in URLs, log lines, and external SDK clients. Accepted on input as either the shortuuid form or the canonical UUID form (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
22^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$Per-column coverage for this retailer's stores after the call — stores, with_point, with_real_address, with_phone, with_url, with_source_extras. On a dry run these are the post-rollback figures the run would have produced, measured inside the transaction.
Show child attributes
Show child attributes
One result per submitted row, in request order.
Show child attributes
Show child attributes
Batch-level restatement of every row that raised, for callers that read only the census.