curl --request POST \
--url https://www.merchkit.com/api/v1/vendors/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"merge_key": "vendor_name",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"country": "Denmark"
}
}
'import requests
url = "https://www.merchkit.com/api/v1/vendors/upsert"
payload = {
"merge_key": "vendor_name",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"country": "Denmark"
}
}
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({
merge_key: 'vendor_name',
attributes: {vendor_name: 'Nordic Timber Co.', country: 'Denmark'}
})
};
fetch('https://www.merchkit.com/api/v1/vendors/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://www.merchkit.com/api/v1/vendors/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([
'merge_key' => 'vendor_name',
'attributes' => [
'vendor_name' => 'Nordic Timber Co.',
'country' => 'Denmark'
]
]),
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://www.merchkit.com/api/v1/vendors/upsert"
payload := strings.NewReader("{\n \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\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://www.merchkit.com/api/v1/vendors/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.merchkit.com/api/v1/vendors/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 \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "77e1b2aa-6b3d-4f19-9d10-8c2a5e7f4b21",
"type": "vendor",
"label": "Nordic Timber Co.",
"parent_id": null,
"created_at": "2026-06-02T14:11:09Z",
"updated_at": "2026-07-18T09:30:22Z",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"website": "https://nordictimber.example",
"country": "Denmark",
"lead_time_days": 21
}
}
}{
"data": {
"id": "77e1b2aa-6b3d-4f19-9d10-8c2a5e7f4b21",
"type": "vendor",
"label": "Nordic Timber Co.",
"parent_id": null,
"created_at": "2026-06-02T14:11:09Z",
"updated_at": "2026-07-18T09:30:22Z",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"website": "https://nordictimber.example",
"country": "Denmark",
"lead_time_days": 21
}
}
}{
"error": {
"code": "validation_failed",
"message": "Unknown attribute key(s): colour.",
"documentation_url": "https://docs.merchkit.com/developers/errors#validation_failed",
"is_retriable": false,
"retry_after_seconds": null,
"alternative_action": "List valid attribute keys via GET /v1/attributes?type=product.",
"request_id": "req_01j9x2k8",
"field_errors": [
{
"field": "colour",
"issue": "not_a_defined_attribute"
}
]
}
}{
"error": {
"code": "validation_failed",
"message": "Unknown attribute key(s): colour.",
"documentation_url": "https://docs.merchkit.com/developers/errors#validation_failed",
"is_retriable": false,
"retry_after_seconds": null,
"alternative_action": "List valid attribute keys via GET /v1/attributes?type=product.",
"request_id": "req_01j9x2k8",
"field_errors": [
{
"field": "colour",
"issue": "not_a_defined_attribute"
}
]
}
}Upsert a vendor by your own key
Create-or-update by a customer-named merge key: merge_key names any defined text-family attribute (usually sku) and attributes[merge_key] supplies the value to match — trimmed, case-insensitive. No match → create (201); one match → update (200); more than one match → 409 listing the matching ids. Idempotent for ERP-style pushes: address entities by your key, no UUID mapping table needed.
Scope: write:vendors.
curl --request POST \
--url https://www.merchkit.com/api/v1/vendors/upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"merge_key": "vendor_name",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"country": "Denmark"
}
}
'import requests
url = "https://www.merchkit.com/api/v1/vendors/upsert"
payload = {
"merge_key": "vendor_name",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"country": "Denmark"
}
}
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({
merge_key: 'vendor_name',
attributes: {vendor_name: 'Nordic Timber Co.', country: 'Denmark'}
})
};
fetch('https://www.merchkit.com/api/v1/vendors/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://www.merchkit.com/api/v1/vendors/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([
'merge_key' => 'vendor_name',
'attributes' => [
'vendor_name' => 'Nordic Timber Co.',
'country' => 'Denmark'
]
]),
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://www.merchkit.com/api/v1/vendors/upsert"
payload := strings.NewReader("{\n \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\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://www.merchkit.com/api/v1/vendors/upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.merchkit.com/api/v1/vendors/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 \"merge_key\": \"vendor_name\",\n \"attributes\": {\n \"vendor_name\": \"Nordic Timber Co.\",\n \"country\": \"Denmark\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "77e1b2aa-6b3d-4f19-9d10-8c2a5e7f4b21",
"type": "vendor",
"label": "Nordic Timber Co.",
"parent_id": null,
"created_at": "2026-06-02T14:11:09Z",
"updated_at": "2026-07-18T09:30:22Z",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"website": "https://nordictimber.example",
"country": "Denmark",
"lead_time_days": 21
}
}
}{
"data": {
"id": "77e1b2aa-6b3d-4f19-9d10-8c2a5e7f4b21",
"type": "vendor",
"label": "Nordic Timber Co.",
"parent_id": null,
"created_at": "2026-06-02T14:11:09Z",
"updated_at": "2026-07-18T09:30:22Z",
"attributes": {
"vendor_name": "Nordic Timber Co.",
"website": "https://nordictimber.example",
"country": "Denmark",
"lead_time_days": 21
}
}
}{
"error": {
"code": "validation_failed",
"message": "Unknown attribute key(s): colour.",
"documentation_url": "https://docs.merchkit.com/developers/errors#validation_failed",
"is_retriable": false,
"retry_after_seconds": null,
"alternative_action": "List valid attribute keys via GET /v1/attributes?type=product.",
"request_id": "req_01j9x2k8",
"field_errors": [
{
"field": "colour",
"issue": "not_a_defined_attribute"
}
]
}
}{
"error": {
"code": "validation_failed",
"message": "Unknown attribute key(s): colour.",
"documentation_url": "https://docs.merchkit.com/developers/errors#validation_failed",
"is_retriable": false,
"retry_after_seconds": null,
"alternative_action": "List valid attribute keys via GET /v1/attributes?type=product.",
"request_id": "req_01j9x2k8",
"field_errors": [
{
"field": "colour",
"issue": "not_a_defined_attribute"
}
]
}
}Authorizations
Workspace-scoped API key (mk_live_...). Each operation lists the scope it requires in x-required-scopes. See the Authentication guide for the full scope table and key management.
Body
A defined text-family attribute key to match on (e.g. sku).
Map of attribute key → new value (sparse — only keys being written). Scalars set the value; null OR "" clears it (the key then disappears from reads). A single-reference key accepts a bare UUID string or an {id} object; a list-reference key accepts an array of either — list writes REPLACE the current set. Read stubs re-sent verbatim are valid writes (extra stub fields like type/label are ignored). Unknown keys are a 400; select values must be one of the attribute's acceptable_values.
Show child attributes
Show child attributes
Response
An existing vendor matched and was updated (full resource).
The one shape for every entity read and mutation response. System fields live at the top level; every customer-defined key lives in attributes, so customer keys can never collide with system ones.
Show child attributes
Show child attributes
{
"id": "9b2f6c1e-8a04-4c6e-b0d3-5f2f6f7a9e21",
"type": "product",
"label": "Aria Oak Dining Table",
"parent_id": null,
"created_at": "2026-06-02T14:11:09Z",
"updated_at": "2026-07-18T09:30:22Z",
"attributes": {
"sku": "ARIA-DT-72",
"product_name": "Aria Oak Dining Table",
"price": 1299,
"material": "White Oak",
"vendor": {
"id": "77e1b2aa-6b3d-4f19-9d10-8c2a5e7f4b21",
"type": "vendor",
"label": "Nordic Timber Co."
},
"category": {
"id": "c41d09f3-2e85-47a6-b93c-d5f8e1a20c67",
"type": "category",
"label": "Dining Tables"
},
"gallery_images": [
{
"id": "f0a1b2c3-d4e5-4f60-8a7b-9c0d1e2f3a4b",
"type": "image",
"label": "aria-hero.jpg"
},
{
"id": "f0a2c3d4-e5f6-4a70-9b8c-0d1e2f3a4b5c",
"type": "image",
"label": "aria-detail.jpg"
},
{
"id": "f0a3d4e5-f6a7-4b80-8c9d-1e2f3a4b5c6d",
"type": "image",
"label": "aria-side.jpg"
},
{
"id": "f0a4e5f6-a7b8-4c90-9dae-2f3a4b5c6d7e",
"type": "image",
"label": "aria-lifestyle.jpg"
},
{
"id": "f0a5f6a7-b8c9-4da0-8ebf-3a4b5c6d7e8f",
"type": "image",
"label": "aria-packshot.jpg"
}
]
}
}
Was this page helpful?