curl --request POST \
--url https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert \
--header 'Content-Type: application/json' \
--header 'x-client-key: <api-key>' \
--header 'x-client-secret: <api-key>' \
--data '
{
"values": [
{
"locationExternalId": "STORE-1042",
"attributeExternalId": "REGION",
"value": {
"type": "single_select",
"selected": "b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d"
}
},
{
"locationExternalId": "STORE-1042",
"attributeName": "Drive Thru Lanes",
"value": {
"type": "number",
"value": 2
}
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeExternalId": "REMODEL_DATE",
"value": {
"type": "date",
"value": "2026-09-01"
}
}
]
}
'import requests
url = "https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert"
payload = { "values": [
{
"locationExternalId": "STORE-1042",
"attributeExternalId": "REGION",
"value": {
"type": "single_select",
"selected": "b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d"
}
},
{
"locationExternalId": "STORE-1042",
"attributeName": "Drive Thru Lanes",
"value": {
"type": "number",
"value": 2
}
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeExternalId": "REMODEL_DATE",
"value": {
"type": "date",
"value": "2026-09-01"
}
}
] }
headers = {
"x-client-key": "<api-key>",
"x-client-secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-client-key': '<api-key>',
'x-client-secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
values: [
{
locationExternalId: 'STORE-1042',
attributeExternalId: 'REGION',
value: {type: 'single_select', selected: 'b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d'}
},
{
locationExternalId: 'STORE-1042',
attributeName: 'Drive Thru Lanes',
value: {type: 'number', value: 2}
},
{
locationId: '46715d09-1628-4b1a-a12d-f9e0bbcdbc3c',
attributeExternalId: 'REMODEL_DATE',
value: {type: 'date', value: '2026-09-01'}
}
]
})
};
fetch('https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-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([
'values' => [
[
'locationExternalId' => 'STORE-1042',
'attributeExternalId' => 'REGION',
'value' => [
'type' => 'single_select',
'selected' => 'b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d'
]
],
[
'locationExternalId' => 'STORE-1042',
'attributeName' => 'Drive Thru Lanes',
'value' => [
'type' => 'number',
'value' => 2
]
],
[
'locationId' => '46715d09-1628-4b1a-a12d-f9e0bbcdbc3c',
'attributeExternalId' => 'REMODEL_DATE',
'value' => [
'type' => 'date',
'value' => '2026-09-01'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-client-key: <api-key>",
"x-client-secret: <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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert"
payload := strings.NewReader("{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-client-key", "<api-key>")
req.Header.Add("x-client-secret", "<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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert")
.header("x-client-key", "<api-key>")
.header("x-client-secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-client-key"] = '<api-key>'
request["x-client-secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeId": "1f6c9e0a-2b7d-4d5e-9a11-8c3f5b6d7e88"
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeId": "2a7d0f1b-3c8e-4e6f-9b22-9d4a6c7e8f99"
}
],
"extra_meta": {
"message": "Values upserted"
}
}Bulk Upsert Attribute Values
Writes up to 2,000 location/attribute values per call. This is the endpoint an HRIS or store-master system calls on a schedule to keep Xenia’s location profile in sync.
Addressing the location: exactly one of locationExternalId or locationId.
Addressing the attribute: at least one of attributeExternalId, attributeId, or attributeName (resolved in that order).
The value must match the attribute’s declared type, and for select types every option id must exist on the definition — otherwise the call fails with 400.
The whole batch is applied in a single version-guarded write: if any row fails, nothing is written. Setting a value replaces any previous value for that location/attribute pair. There is no “clear” variant on this endpoint — an attribute simply has no entry for a location until you set one.
Requires the Public API and Location Attributes workspace features plus CAN_MANAGE_LOCATIONS.
curl --request POST \
--url https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert \
--header 'Content-Type: application/json' \
--header 'x-client-key: <api-key>' \
--header 'x-client-secret: <api-key>' \
--data '
{
"values": [
{
"locationExternalId": "STORE-1042",
"attributeExternalId": "REGION",
"value": {
"type": "single_select",
"selected": "b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d"
}
},
{
"locationExternalId": "STORE-1042",
"attributeName": "Drive Thru Lanes",
"value": {
"type": "number",
"value": 2
}
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeExternalId": "REMODEL_DATE",
"value": {
"type": "date",
"value": "2026-09-01"
}
}
]
}
'import requests
url = "https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert"
payload = { "values": [
{
"locationExternalId": "STORE-1042",
"attributeExternalId": "REGION",
"value": {
"type": "single_select",
"selected": "b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d"
}
},
{
"locationExternalId": "STORE-1042",
"attributeName": "Drive Thru Lanes",
"value": {
"type": "number",
"value": 2
}
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeExternalId": "REMODEL_DATE",
"value": {
"type": "date",
"value": "2026-09-01"
}
}
] }
headers = {
"x-client-key": "<api-key>",
"x-client-secret": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-client-key': '<api-key>',
'x-client-secret': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
values: [
{
locationExternalId: 'STORE-1042',
attributeExternalId: 'REGION',
value: {type: 'single_select', selected: 'b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d'}
},
{
locationExternalId: 'STORE-1042',
attributeName: 'Drive Thru Lanes',
value: {type: 'number', value: 2}
},
{
locationId: '46715d09-1628-4b1a-a12d-f9e0bbcdbc3c',
attributeExternalId: 'REMODEL_DATE',
value: {type: 'date', value: '2026-09-01'}
}
]
})
};
fetch('https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-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([
'values' => [
[
'locationExternalId' => 'STORE-1042',
'attributeExternalId' => 'REGION',
'value' => [
'type' => 'single_select',
'selected' => 'b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d'
]
],
[
'locationExternalId' => 'STORE-1042',
'attributeName' => 'Drive Thru Lanes',
'value' => [
'type' => 'number',
'value' => 2
]
],
[
'locationId' => '46715d09-1628-4b1a-a12d-f9e0bbcdbc3c',
'attributeExternalId' => 'REMODEL_DATE',
'value' => [
'type' => 'date',
'value' => '2026-09-01'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-client-key: <api-key>",
"x-client-secret: <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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert"
payload := strings.NewReader("{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-client-key", "<api-key>")
req.Header.Add("x-client-secret", "<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://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert")
.header("x-client-key", "<api-key>")
.header("x-client-secret", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.xenia.team/api/v1/mgt/enterprise/workspaces/{workspaceId}/location-attribute-values/bulk-upsert")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-client-key"] = '<api-key>'
request["x-client-secret"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"values\": [\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeExternalId\": \"REGION\",\n \"value\": {\n \"type\": \"single_select\",\n \"selected\": \"b0a1c2d3-4e5f-4a6b-8c9d-0e1f2a3b4c5d\"\n }\n },\n {\n \"locationExternalId\": \"STORE-1042\",\n \"attributeName\": \"Drive Thru Lanes\",\n \"value\": {\n \"type\": \"number\",\n \"value\": 2\n }\n },\n {\n \"locationId\": \"46715d09-1628-4b1a-a12d-f9e0bbcdbc3c\",\n \"attributeExternalId\": \"REMODEL_DATE\",\n \"value\": {\n \"type\": \"date\",\n \"value\": \"2026-09-01\"\n }\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeId": "1f6c9e0a-2b7d-4d5e-9a11-8c3f5b6d7e88"
},
{
"locationId": "46715d09-1628-4b1a-a12d-f9e0bbcdbc3c",
"attributeId": "2a7d0f1b-3c8e-4e6f-9b22-9d4a6c7e8f99"
}
],
"extra_meta": {
"message": "Values upserted"
}
}Path Parameters
UUID of the workspace. Must be the workspace your API key is bound to.
Body
1 - 2000 elementsShow child attributes
Show child attributes
Response
One row per input row, with the resolved Xenia ids.