curl --request PATCH \
--url https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"is_pinned": true,
"languages": [
"es"
],
"purpose": "gs1:quickStartGuide"
}
'import requests
url = "https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}"
payload = {
"is_pinned": True,
"languages": ["es"],
"purpose": "gs1:quickStartGuide"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({is_pinned: true, languages: ['es'], purpose: 'gs1:quickStartGuide'})
};
fetch('https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}', 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/products/api/v1/products/{gtin}/documents/{document_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'is_pinned' => true,
'languages' => [
'es'
],
'purpose' => 'gs1:quickStartGuide'
]),
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/products/api/v1/products/{gtin}/documents/{document_id}"
payload := strings.NewReader("{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}"
response = http.request(request)
puts response.read_body{
"description": "",
"file_size_bytes": 245098,
"format": "pdf",
"id": "2N4kq7ZxYQ8vR3sT1bWnCp",
"is_active": true,
"is_pinned": false,
"labels": [
"setup"
],
"languages": [
"es"
],
"markets": [],
"media_type": "application/pdf",
"purpose": "gs1:quickStartGuide",
"sort_order": 0,
"title": "Quick start guide",
"url": "https://www.closient.com/products/api/v1/products/00614141999996/documents/2N4kq7ZxYQ8vR3sT1bWnCp/download"
}{
"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"
}Update a document's facets and metadata
Only supplied fields change. To replace the underlying file or link, create a new document and delete the old one — a JSON body has no way to carry a file upload, and django-ninja does not support file uploads on PATCH without a project-wide middleware change. Requires the organization.contribute_organization permission on the membership owning the GTIN.
curl --request PATCH \
--url https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"is_pinned": true,
"languages": [
"es"
],
"purpose": "gs1:quickStartGuide"
}
'import requests
url = "https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}"
payload = {
"is_pinned": True,
"languages": ["es"],
"purpose": "gs1:quickStartGuide"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({is_pinned: true, languages: ['es'], purpose: 'gs1:quickStartGuide'})
};
fetch('https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}', 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/products/api/v1/products/{gtin}/documents/{document_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'is_pinned' => true,
'languages' => [
'es'
],
'purpose' => 'gs1:quickStartGuide'
]),
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/products/api/v1/products/{gtin}/documents/{document_id}"
payload := strings.NewReader("{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/products/api/v1/products/{gtin}/documents/{document_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"is_pinned\": true,\n \"languages\": [\n \"es\"\n ],\n \"purpose\": \"gs1:quickStartGuide\"\n}"
response = http.request(request)
puts response.read_body{
"description": "",
"file_size_bytes": 245098,
"format": "pdf",
"id": "2N4kq7ZxYQ8vR3sT1bWnCp",
"is_active": true,
"is_pinned": false,
"labels": [
"setup"
],
"languages": [
"es"
],
"markets": [],
"media_type": "application/pdf",
"purpose": "gs1:quickStartGuide",
"sort_order": 0,
"title": "Quick start guide",
"url": "https://www.closient.com/products/api/v1/products/00614141999996/documents/2N4kq7ZxYQ8vR3sT1bWnCp/download"
}{
"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
GTIN barcode (8, 12, 13, or 14 digits, with or without separators). Normalized to GTIN-14 for lookup.
^[0-9]{8,14}$UUID (or 22-char shortuuid) of the document row. Must belong to the targeted product.
Body
Facet/metadata patch. To replace the underlying file or link, create a new document instead.
GS1 link type CURIE this document is filed under.
gs1:certificationInfo, gs1:epil, gs1:instructions, gs1:quickStartGuide, gs1:recipeInfo, gs1:safetyInfo, gs1:traceability, gs1:whatsInTheBox At least one BCP 47 language tag.
BCP 47 language tag with a real ISO 639-1 primary subtag (e.g. en, es-MX, fr-CA). Matching against a document's stored languages falls back to the primary subtag, so es-MX matches a document filed under plain es.
^(?:aa|ab|ae|af|ak|am|an|ar|as|av|ay|az|ba|be|bg|bi|bm|bn|bo|br|bs|ca|ce|ch|co|cr|cs|cu|cv|cy|da|de|dv|dz|ee|el|en|eo|es|et|eu|fa|ff|fi|fj|fo|fr|fy|ga|gd|gl|gn|gu|gv|ha|he|hi|ho|hr|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|io|is|it|iu|ja|jv|ka|kg|ki|kj|kk|kl|km|kn|ko|kr|ks|ku|kv|kw|ky|la|lb|lg|li|ln|lo|lt|lu|lv|mg|mh|mi|mk|ml|mn|mr|ms|mt|my|na|nb|nd|ne|ng|nl|nn|no|nr|nv|ny|oc|oj|om|or|os|pa|pi|pl|ps|pt|qu|rm|rn|ro|ru|rw|sa|sc|sd|se|sg|sh|si|sk|sl|sm|sn|so|sq|sr|ss|st|su|sv|sw|ta|te|tg|th|ti|tk|tl|tn|to|tr|ts|tt|tw|ty|ug|uk|ur|uz|ve|vi|vo|wa|wo|xh|yi|yo|za|zh|zu)(-[A-Z][a-z]{3})?(-([A-Z]{2}|\d{3}))?(-([0-9A-Za-z]{5,8}|\d[0-9A-Za-z]{3}))*$ISO 3166-1 alpha-2 codes. Empty means all markets.
ISO 3166-1 alpha-2 market code (e.g. US, CA). Unlike the filter on the list endpoint (which accepts any 2-letter code, matching nothing for an unassigned one), this is data a brand is asserting about the document, so it is checked against the real assigned set — same rule as apps.core.api.types.CountryCode, but published as an explicit enum here (rather than a loose pattern) so the contract-fuzz suite cannot generate a schema-compliant-but-unassigned code and get a 422 for it (C-4684 contract ratchet; the underlying CountryCode/pattern mismatch is pre-existing, wider debt — see the baselined POST /account/api/v1/organizations — not something this ticket reopens).
AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, YE, YT, ZA, ZM, ZW At most 10 brand-owned tags, each at most 40 characters.
101 - 40Document title.
255Free-text description.
At most one pinned document per product.
Tiebreaker within a purpose group; lower sorts first.
x >= 0Set false to soft-hide the document from every public surface (the page, the Catalog API, the resolver linkset) without deleting it; set true to restore it.
Response
OK
A single document, as returned by the public Catalog API.
Document identifier.
22^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$GS1 link type CURIE describing what this document is, e.g. gs1:instructions.
Document title.
Resolvable public URL for this document — never a signed or expiring one. For an uploaded file this is Closient's own download-redirect endpoint (permanent; it re-signs on every hit). For a brand-supplied link this is that link verbatim.
Full IANA media type, e.g. application/pdf.
Presentation bucket derived from media_type.
pdf, video, web_page, image, other BCP 47 language tags this document is available in.
ISO 3166-1 alpha-2 codes. Empty means all markets.
Brand-owned free-text tags, normalized to lowercase.
Featured above the rest of the section.
Tiebreaker within a purpose group; lower sorts first.
Inactive documents are excluded from every public surface.
Free-text description.
Size of an uploaded file. Null for a link-only document.