curl --request PATCH \
--url https://api.print.app/v2/option-sets/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"config": {},
"addProductIds": [
"<string>"
],
"removeProductIds": [
"<string>"
],
"dryRun": true
}
'import requests
url = "https://api.print.app/v2/option-sets/{id}"
payload = {
"title": "<string>",
"config": {},
"addProductIds": ["<string>"],
"removeProductIds": ["<string>"],
"dryRun": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
config: {},
addProductIds: ['<string>'],
removeProductIds: ['<string>'],
dryRun: true
})
};
fetch('https://api.print.app/v2/option-sets/{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://api.print.app/v2/option-sets/{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([
'title' => '<string>',
'config' => [
],
'addProductIds' => [
'<string>'
],
'removeProductIds' => [
'<string>'
],
'dryRun' => true
]),
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.print.app/v2/option-sets/{id}"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.print.app/v2/option-sets/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.print.app/v2/option-sets/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}"
response = http.request(request)
puts response.read_body{
"object": "option_set_change",
"dryRun": false,
"optionSetId": "2f7c9e1a-4b8d-4e2f-9a61-0c3d5b7e8f10",
"title": "Flyers",
"currency": "USD",
"created": true,
"deleted": false,
"changes": [
{
"productId": "8123456789",
"title": "Flyers",
"change": "added",
"fromOptionSetId": null,
"ok": true,
"error": null
}
],
"failed": 0,
"fields": [
{
"id": "size",
"label": "Size",
"type": "select-one",
"role": "size",
"source": {
"role": "size"
},
"reference": "po:role/size",
"sends": "choice id as value, label as text",
"choices": [
{
"value": "a5",
"label": "A5",
"selected": true
},
{
"value": "a4",
"label": "A4",
"selected": false
}
]
}
],
"warnings": []
}{
"error": true,
"code": "invalid_request",
"message": "width is required.",
"param": "width",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unauthorized",
"message": "Missing or invalid secret key. Send it as \"Authorization: Bearer sk_…\".",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "account_suspended",
"message": "This account is suspended. Settle the outstanding invoice in the Print.App dashboard.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "not_found",
"message": "Not found.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "folder_not_empty",
"message": "The folder still holds designs. Remove them from it (or delete them) first.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "payload_too_large",
"message": "The file at url is larger than 50 MB.",
"param": "url",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unsupported_media_type",
"message": "Send the request body as JSON, with \"Content-Type: application/json\".",
"requestId": "FZn-uhymjoEEJow="
}{
"message": "Too Many Requests"
}{
"error": true,
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, contact support with the requestId.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "service_unavailable",
"message": "Searching the library is unavailable right now. Try again in a minute.",
"requestId": "FZn-uhymjoEEJow="
}Update an option set
Changes an option set’s options and prices (config), its title, or the products it is on. The option set is republished to every product it stays on; changes lists each product. With dryRun, nothing is written.
curl --request PATCH \
--url https://api.print.app/v2/option-sets/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"config": {},
"addProductIds": [
"<string>"
],
"removeProductIds": [
"<string>"
],
"dryRun": true
}
'import requests
url = "https://api.print.app/v2/option-sets/{id}"
payload = {
"title": "<string>",
"config": {},
"addProductIds": ["<string>"],
"removeProductIds": ["<string>"],
"dryRun": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
config: {},
addProductIds: ['<string>'],
removeProductIds: ['<string>'],
dryRun: true
})
};
fetch('https://api.print.app/v2/option-sets/{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://api.print.app/v2/option-sets/{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([
'title' => '<string>',
'config' => [
],
'addProductIds' => [
'<string>'
],
'removeProductIds' => [
'<string>'
],
'dryRun' => true
]),
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.print.app/v2/option-sets/{id}"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.print.app/v2/option-sets/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.print.app/v2/option-sets/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"config\": {},\n \"addProductIds\": [\n \"<string>\"\n ],\n \"removeProductIds\": [\n \"<string>\"\n ],\n \"dryRun\": true\n}"
response = http.request(request)
puts response.read_body{
"object": "option_set_change",
"dryRun": false,
"optionSetId": "2f7c9e1a-4b8d-4e2f-9a61-0c3d5b7e8f10",
"title": "Flyers",
"currency": "USD",
"created": true,
"deleted": false,
"changes": [
{
"productId": "8123456789",
"title": "Flyers",
"change": "added",
"fromOptionSetId": null,
"ok": true,
"error": null
}
],
"failed": 0,
"fields": [
{
"id": "size",
"label": "Size",
"type": "select-one",
"role": "size",
"source": {
"role": "size"
},
"reference": "po:role/size",
"sends": "choice id as value, label as text",
"choices": [
{
"value": "a5",
"label": "A5",
"selected": true
},
{
"value": "a4",
"label": "A4",
"selected": false
}
]
}
],
"warnings": []
}{
"error": true,
"code": "invalid_request",
"message": "width is required.",
"param": "width",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unauthorized",
"message": "Missing or invalid secret key. Send it as \"Authorization: Bearer sk_…\".",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "account_suspended",
"message": "This account is suspended. Settle the outstanding invoice in the Print.App dashboard.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "not_found",
"message": "Not found.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "folder_not_empty",
"message": "The folder still holds designs. Remove them from it (or delete them) first.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "payload_too_large",
"message": "The file at url is larger than 50 MB.",
"param": "url",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unsupported_media_type",
"message": "Send the request body as JSON, with \"Content-Type: application/json\".",
"requestId": "FZn-uhymjoEEJow="
}{
"message": "Too Many Requests"
}{
"error": true,
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, contact support with the requestId.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "service_unavailable",
"message": "Searching the library is unavailable right now. Try again in a minute.",
"requestId": "FZn-uhymjoEEJow="
}Authorizations
A secret key from Print.App admin → Domains → Secret keys, sent as Authorization: Bearer sk_…. Server-side only.
Path Parameters
The option set id.
Body
What to change. The option set is republished to every product it stays on.
Its name in Print Options. Default: the config's title.
1 - 200The option set itself: Print Options' JSON (version "2.0"), as in the option set JSON reference. Replaces the whole option set.
Products to publish it to as well. A product on another option set moves to this one.
1 - 50 elements^(gid://shopify/Product/)?[0-9]{1,20}$Products to take it off: their pages go back to the theme's own add-to-cart.
1 - 50 elements^(gid://shopify/Product/)?[0-9]{1,20}$Check everything and say what would change, writing nothing.
Response
OK.
What a create, update or delete did to each product, or would do (dryRun).
"option_set_change"true when nothing was written.
The option set. null for a dry-run create.
Show child attributes
Show child attributes
Products that did not change.
x >= 0Its fields as connectors address them: give a field's source as a connector's source.
Show child attributes
Show child attributes
Things that will not stop it working but may not be meant.
Show child attributes
Show child attributes