curl --request PATCH \
--url https://api.shoppex.io/dev/v1/products/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "DYNAMIC",
"dynamic_webhook": "https://example.com/updated",
"delivery_instructions": {
"required": false,
"label": "Setup note",
"max_length": 300
},
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": false
}
]
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'DYNAMIC',
dynamic_webhook: 'https://example.com/updated',
delivery_instructions: {required: false, label: 'Setup note', max_length: 300},
custom_fields: [{name: 'Display Name', type: 'text', required: false}]
})
};
fetch('https://api.shoppex.io/dev/v1/products/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.shoppex.io/dev/v1/products/{id}"
payload = {
"type": "DYNAMIC",
"dynamic_webhook": "https://example.com/updated",
"delivery_instructions": {
"required": False,
"label": "Setup note",
"max_length": 300
},
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": False
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.shoppex.io/dev/v1/products/{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([
'type' => 'DYNAMIC',
'dynamic_webhook' => 'https://example.com/updated',
'delivery_instructions' => [
'required' => false,
'label' => 'Setup note',
'max_length' => 300
],
'custom_fields' => [
[
'name' => 'Display Name',
'type' => 'text',
'required' => false
]
]
]),
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.shoppex.io/dev/v1/products/{id}"
payload := strings.NewReader("{\n \"type\": \"DYNAMIC\",\n \"dynamic_webhook\": \"https://example.com/updated\",\n \"delivery_instructions\": {\n \"required\": false,\n \"label\": \"Setup note\",\n \"max_length\": 300\n },\n \"custom_fields\": [\n {\n \"name\": \"Display Name\",\n \"type\": \"text\",\n \"required\": false\n }\n ]\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))
}{
"data": {
"id": "prod_db_1",
"uniqid": "prod_1",
"shop_id": "shop_1",
"slug": null,
"type": "DYNAMIC",
"subtype": null,
"title": "Product One",
"currency": "USD",
"price": 19.99,
"price_display": 19.99,
"description": "Product description",
"description_tabs": [
{
"title": "Features",
"content": "<p>Instant delivery, 24/7 support.</p>"
}
],
"gateways": [
"PAYPAL"
],
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": false
}
],
"delivery_instructions": {
"enabled": true,
"required": false,
"label": "Setup note",
"max_length": 300
},
"quantity_min": 1,
"quantity_max": -1,
"quantity_warning": 0,
"stock": 0,
"stock_delimiter": "\n",
"unlisted": false,
"private": false,
"on_hold": false,
"sort_priority": 0,
"crypto_confirmations": 1,
"max_risk_level": 100,
"block_vpn_proxies": false,
"delivery_text": "",
"delivery_time": null,
"service_text": "",
"dynamic_webhook": "https://example.com/updated",
"watermark_enabled": true,
"image_id": null,
"image_url": null,
"variants": [],
"created_at": 1711965600,
"updated_at": 1711969200
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}Update product
Updates an existing product. For base SERIALS products, sending the serials field replaces the full base-product serial inventory. Use /dev/v1/products/{id}/serials when you need append/remove behavior instead of full replacement.
curl --request PATCH \
--url https://api.shoppex.io/dev/v1/products/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "DYNAMIC",
"dynamic_webhook": "https://example.com/updated",
"delivery_instructions": {
"required": false,
"label": "Setup note",
"max_length": 300
},
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": false
}
]
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'DYNAMIC',
dynamic_webhook: 'https://example.com/updated',
delivery_instructions: {required: false, label: 'Setup note', max_length: 300},
custom_fields: [{name: 'Display Name', type: 'text', required: false}]
})
};
fetch('https://api.shoppex.io/dev/v1/products/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.shoppex.io/dev/v1/products/{id}"
payload = {
"type": "DYNAMIC",
"dynamic_webhook": "https://example.com/updated",
"delivery_instructions": {
"required": False,
"label": "Setup note",
"max_length": 300
},
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": False
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.shoppex.io/dev/v1/products/{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([
'type' => 'DYNAMIC',
'dynamic_webhook' => 'https://example.com/updated',
'delivery_instructions' => [
'required' => false,
'label' => 'Setup note',
'max_length' => 300
],
'custom_fields' => [
[
'name' => 'Display Name',
'type' => 'text',
'required' => false
]
]
]),
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.shoppex.io/dev/v1/products/{id}"
payload := strings.NewReader("{\n \"type\": \"DYNAMIC\",\n \"dynamic_webhook\": \"https://example.com/updated\",\n \"delivery_instructions\": {\n \"required\": false,\n \"label\": \"Setup note\",\n \"max_length\": 300\n },\n \"custom_fields\": [\n {\n \"name\": \"Display Name\",\n \"type\": \"text\",\n \"required\": false\n }\n ]\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))
}{
"data": {
"id": "prod_db_1",
"uniqid": "prod_1",
"shop_id": "shop_1",
"slug": null,
"type": "DYNAMIC",
"subtype": null,
"title": "Product One",
"currency": "USD",
"price": 19.99,
"price_display": 19.99,
"description": "Product description",
"description_tabs": [
{
"title": "Features",
"content": "<p>Instant delivery, 24/7 support.</p>"
}
],
"gateways": [
"PAYPAL"
],
"custom_fields": [
{
"name": "Display Name",
"type": "text",
"required": false
}
],
"delivery_instructions": {
"enabled": true,
"required": false,
"label": "Setup note",
"max_length": 300
},
"quantity_min": 1,
"quantity_max": -1,
"quantity_warning": 0,
"stock": 0,
"stock_delimiter": "\n",
"unlisted": false,
"private": false,
"on_hold": false,
"sort_priority": 0,
"crypto_confirmations": 1,
"max_risk_level": 100,
"block_vpn_proxies": false,
"delivery_text": "",
"delivery_time": null,
"service_text": "",
"dynamic_webhook": "https://example.com/updated",
"watermark_enabled": true,
"image_id": null,
"image_url": null,
"variants": [],
"created_at": 1711965600,
"updated_at": 1711969200
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"details": [
{
"field": "<string>",
"message": "<string>"
}
],
"subcode": "<string>"
}
}Authorizations
Use your Shoppex API key in the Authorization header.
Path Parameters
Body
One plain-text line shown under the product title on storefronts (max 200 characters). Send null or an empty string to clear.
200Show child attributes
Show child attributes
Merchant-defined public key/value pairs surfaced on the storefront product wire. Flat strings only; at most 50 keys. Send {} to clear.
Show child attributes
Show child attributes
SERIALS, FILE, SERVICE, DYNAMIC, INFO_CARD, SUBSCRIPTION, PHYSICAL, GAMING_RCON, SUBSCRIPTION_V2 SINGLE, MULTIPLE, RANDOM, REPLACEMENT, SERIALS, FILE, SERVICE, DYNAMIC One key per item; an item with line breaks is split into one key per line. Keys longer than 10,000 characters reject the request.
Keep repeated keys of serials as separate stock units (placeholder stock). Default false: repeats are dropped and counted in serials_import.duplicates_in_batch.
URL Shoppex calls to deliver a DYNAMIC product. A new value must be a public https URL without a username or password, at most 2048 characters. A product that stores an http URL keeps it while the field is omitted or sent back unchanged. Send null or an empty string to remove the webhook.
Keys are one per line; commas and semicolons stay part of the key. newline or a line break (as GET returns it).
newline,
Keys are one per line; commas and semicolons stay part of the key. newline or a line break (as GET returns it).
newline,
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Risk score ceiling, 0-100. 100 lets every buyer through.
Risk score ceiling, 0-100. 100 lets every buyer through.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
DAY, WEEK, MONTH, YEAR DAY, WEEK, MONTH, YEAR minutes, hours, days minutes, hours, days Show child attributes
Show child attributes
Discord server (guild) id. On PATCH, changing this value clears stored primary, required and additional role IDs unless replacements are supplied in the same request.
Discord server (guild) id. On PATCH, changing this value clears stored primary, required and additional role IDs unless replacements are supplied in the same request.
^(?:[1-9][0-9]*|)$Existing Discord role required before purchase, independent of delivery. A numeric unsigned 64-bit Discord snowflake (maximum 18446744073709551615). Requires discord_server_id. Clear with null or an empty string. Buyers must sign in to the hosted shop and connect Discord in their account.
^(?:[1-9][0-9]*|)$Additional Discord role ids (max 20).
Additional Discord role ids (max 20).
USE_STORE_DEFAULT, CUSTOM, DISABLED USE_STORE_DEFAULT, CUSTOM, DISABLED Search title of the product page (max 70 characters after collapsing whitespace). Null or blank clears it; the storefront then uses the product title.
Search description of the product page (max 160 characters after collapsing whitespace). Null or blank clears it.
Gallery images in display order, at most 10: ids from POST /dev/v1/media (images of this shop). The first becomes the main image. Replaces the whole gallery; [] removes every image.
10