import Shoply from "@shoplygg/node";
const shoply = new Shoply({ apiKey: process.env.SHOPLY_API_KEY });
const result = await shoply.products.update("PRODUCT_ID", {
name: "Pro licence",
variants: [],
});<?php
require 'vendor/autoload.php';
$shoply = new Shoply\Client(['apiKey' => getenv('SHOPLY_API_KEY')]);
$result = $shoply->products->update('PRODUCT_ID', [
'name' => 'Pro licence',
'variants' => [],
]);
import shoply
client = shoply.Client() # reads SHOPLY_API_KEY
result = client.products.update("PRODUCT_ID", name="Pro licence", variants=[])
curl --request PUT \
--url https://api.shoply.gg/v1/stores/{storeId}/products/{productId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Pro licence",
"variants": [
{
"label": "1 month",
"price": 9.99,
"id": "<string>",
"minQuantity": 1,
"maxQuantity": -1,
"deliveryMethods": [],
"stockMode": 2,
"stock": 123,
"deliveryInstructions": "<string>",
"dynamicUrl": "<string>",
"customFieldsOn": true,
"customFields": [
{
"label": "Discord Username",
"key": "discord_username",
"type": "text",
"required": false,
"placeholder": "<string>",
"hint": "<string>",
"options": [
"<string>"
]
}
],
"blockedPaymentMethods": [
"<string>"
],
"sort": 123
}
],
"slug": "<string>",
"description": "<string>",
"visibility": "public",
"deliveryInstructions": "<string>",
"categoryIds": [
"<string>"
],
"isBestSeller": false,
"badges": [
{
"label": "<string>",
"kind": "label"
}
],
"features": [
{
"name": "<string>",
"features": [
"<string>"
]
}
],
"blockedPaymentMethods": [
"<string>"
],
"paymentFees": {
"stripe": {
"percent": 3,
"fixed": 0.3
}
},
"salesWindow": "all",
"metadata": {},
"seoTitle": "<string>",
"seoDescription": "<string>",
"expectedUpdatedAt": "<string>"
}
'package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.shoply.gg/v1/stores/{storeId}/products/{productId}"
payload := strings.NewReader("{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.shoply.gg/v1/stores/{storeId}/products/{productId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.shoply.gg/v1/stores/{storeId}/products/{productId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": null,
"code": null,
"data": {
"id": "f463e265-097a-4235-bc2f-5239b19a9874",
"storeId": "743d82fb-6aad-44bb-ab00-d54c4b0eecb7",
"name": "Northwind Pro",
"slug": "northwind-pro",
"description": "A licence for Northwind Pro, sent the moment you pay.",
"seoTitle": "",
"seoDescription": "",
"image": "",
"imageUrl": null,
"visibility": "public",
"sort": 0,
"isBestSeller": false,
"badges": [
{
"label": "New",
"color": "green",
"kind": "label"
}
],
"features": [
{
"type": "feature",
"name": "Lifetime updates"
},
{
"type": "category",
"name": "Support",
"features": [
"Email",
"Discord"
]
}
],
"deliveryInstructions": "",
"blockedPaymentMethods": [],
"paymentFees": [],
"showViewers": false,
"showSales": false,
"salesWindow": "all",
"saleNotifications": false,
"metadata": "",
"dateCreated": "2026-10-22T16:20:18Z",
"updatedAt": "2026-10-22T16:20:18.678Z",
"categoryIds": [
"e31dbb6f-c007-4e28-b0f8-49c4b432cfec"
],
"unitsSold": 0,
"revenue": 0,
"lowPrice": 9.99,
"highPrice": 79,
"totalStock": 0,
"untrackedVariants": 0,
"imageCount": 0,
"images": [],
"variants": [
{
"id": "00e1d395-9ba2-4db3-900a-cb5e2811a2a9",
"label": "1 month",
"price": 9.99,
"duration": "1 month",
"durationDays": 30,
"stockMode": 2,
"minQuantity": 1,
"maxQuantity": -1,
"stock": 0,
"keyCount": 0,
"deliveryMethods": [
"license_keys"
],
"deliveryType": "license_keys",
"deliveryInstructions": "",
"dynamicUrl": "",
"customFieldsOn": false,
"customFields": [],
"sort": 0,
"blockedPaymentMethods": []
},
{
"id": "defa3f9e-8aaf-423d-a0a6-fe65ab8043d4",
"label": "1 year",
"price": 79,
"duration": "1 year",
"durationDays": 365,
"stockMode": 2,
"minQuantity": 1,
"maxQuantity": -1,
"stock": 0,
"keyCount": 0,
"deliveryMethods": [
"license_keys",
"instructions"
],
"deliveryType": "license_keys",
"deliveryInstructions": "Paste the key under Help, Activate.",
"dynamicUrl": "",
"customFieldsOn": false,
"customFields": [],
"sort": 1,
"blockedPaymentMethods": []
}
],
"runningSubscriptions": 0
}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}Update a product
Saves the product whole, as its editor does: send every field as it should be. variants is the complete list. An entry with an id changes that variant, an entry without one is added, and a variant left out is removed with its unsold stock. A variant buyers have received license keys from cannot be removed.
Read the product first and send it back changed. The fields that say “stay as they are” are the only ones that may be left out.
Needs the products.write permission (Change Products).
import Shoply from "@shoplygg/node";
const shoply = new Shoply({ apiKey: process.env.SHOPLY_API_KEY });
const result = await shoply.products.update("PRODUCT_ID", {
name: "Pro licence",
variants: [],
});<?php
require 'vendor/autoload.php';
$shoply = new Shoply\Client(['apiKey' => getenv('SHOPLY_API_KEY')]);
$result = $shoply->products->update('PRODUCT_ID', [
'name' => 'Pro licence',
'variants' => [],
]);
import shoply
client = shoply.Client() # reads SHOPLY_API_KEY
result = client.products.update("PRODUCT_ID", name="Pro licence", variants=[])
curl --request PUT \
--url https://api.shoply.gg/v1/stores/{storeId}/products/{productId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Pro licence",
"variants": [
{
"label": "1 month",
"price": 9.99,
"id": "<string>",
"minQuantity": 1,
"maxQuantity": -1,
"deliveryMethods": [],
"stockMode": 2,
"stock": 123,
"deliveryInstructions": "<string>",
"dynamicUrl": "<string>",
"customFieldsOn": true,
"customFields": [
{
"label": "Discord Username",
"key": "discord_username",
"type": "text",
"required": false,
"placeholder": "<string>",
"hint": "<string>",
"options": [
"<string>"
]
}
],
"blockedPaymentMethods": [
"<string>"
],
"sort": 123
}
],
"slug": "<string>",
"description": "<string>",
"visibility": "public",
"deliveryInstructions": "<string>",
"categoryIds": [
"<string>"
],
"isBestSeller": false,
"badges": [
{
"label": "<string>",
"kind": "label"
}
],
"features": [
{
"name": "<string>",
"features": [
"<string>"
]
}
],
"blockedPaymentMethods": [
"<string>"
],
"paymentFees": {
"stripe": {
"percent": 3,
"fixed": 0.3
}
},
"salesWindow": "all",
"metadata": {},
"seoTitle": "<string>",
"seoDescription": "<string>",
"expectedUpdatedAt": "<string>"
}
'package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.shoply.gg/v1/stores/{storeId}/products/{productId}"
payload := strings.NewReader("{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.shoply.gg/v1/stores/{storeId}/products/{productId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.shoply.gg/v1/stores/{storeId}/products/{productId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Pro licence\",\n \"variants\": [\n {\n \"label\": \"1 month\",\n \"price\": 9.99,\n \"id\": \"<string>\",\n \"minQuantity\": 1,\n \"maxQuantity\": -1,\n \"deliveryMethods\": [],\n \"stockMode\": 2,\n \"stock\": 123,\n \"deliveryInstructions\": \"<string>\",\n \"dynamicUrl\": \"<string>\",\n \"customFieldsOn\": true,\n \"customFields\": [\n {\n \"label\": \"Discord Username\",\n \"key\": \"discord_username\",\n \"type\": \"text\",\n \"required\": false,\n \"placeholder\": \"<string>\",\n \"hint\": \"<string>\",\n \"options\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"sort\": 123\n }\n ],\n \"slug\": \"<string>\",\n \"description\": \"<string>\",\n \"visibility\": \"public\",\n \"deliveryInstructions\": \"<string>\",\n \"categoryIds\": [\n \"<string>\"\n ],\n \"isBestSeller\": false,\n \"badges\": [\n {\n \"label\": \"<string>\",\n \"kind\": \"label\"\n }\n ],\n \"features\": [\n {\n \"name\": \"<string>\",\n \"features\": [\n \"<string>\"\n ]\n }\n ],\n \"blockedPaymentMethods\": [\n \"<string>\"\n ],\n \"paymentFees\": {\n \"stripe\": {\n \"percent\": 3,\n \"fixed\": 0.3\n }\n },\n \"salesWindow\": \"all\",\n \"metadata\": {},\n \"seoTitle\": \"<string>\",\n \"seoDescription\": \"<string>\",\n \"expectedUpdatedAt\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": null,
"code": null,
"data": {
"id": "f463e265-097a-4235-bc2f-5239b19a9874",
"storeId": "743d82fb-6aad-44bb-ab00-d54c4b0eecb7",
"name": "Northwind Pro",
"slug": "northwind-pro",
"description": "A licence for Northwind Pro, sent the moment you pay.",
"seoTitle": "",
"seoDescription": "",
"image": "",
"imageUrl": null,
"visibility": "public",
"sort": 0,
"isBestSeller": false,
"badges": [
{
"label": "New",
"color": "green",
"kind": "label"
}
],
"features": [
{
"type": "feature",
"name": "Lifetime updates"
},
{
"type": "category",
"name": "Support",
"features": [
"Email",
"Discord"
]
}
],
"deliveryInstructions": "",
"blockedPaymentMethods": [],
"paymentFees": [],
"showViewers": false,
"showSales": false,
"salesWindow": "all",
"saleNotifications": false,
"metadata": "",
"dateCreated": "2026-10-22T16:20:18Z",
"updatedAt": "2026-10-22T16:20:18.678Z",
"categoryIds": [
"e31dbb6f-c007-4e28-b0f8-49c4b432cfec"
],
"unitsSold": 0,
"revenue": 0,
"lowPrice": 9.99,
"highPrice": 79,
"totalStock": 0,
"untrackedVariants": 0,
"imageCount": 0,
"images": [],
"variants": [
{
"id": "00e1d395-9ba2-4db3-900a-cb5e2811a2a9",
"label": "1 month",
"price": 9.99,
"duration": "1 month",
"durationDays": 30,
"stockMode": 2,
"minQuantity": 1,
"maxQuantity": -1,
"stock": 0,
"keyCount": 0,
"deliveryMethods": [
"license_keys"
],
"deliveryType": "license_keys",
"deliveryInstructions": "",
"dynamicUrl": "",
"customFieldsOn": false,
"customFields": [],
"sort": 0,
"blockedPaymentMethods": []
},
{
"id": "defa3f9e-8aaf-423d-a0a6-fe65ab8043d4",
"label": "1 year",
"price": 79,
"duration": "1 year",
"durationDays": 365,
"stockMode": 2,
"minQuantity": 1,
"maxQuantity": -1,
"stock": 0,
"keyCount": 0,
"deliveryMethods": [
"license_keys",
"instructions"
],
"deliveryType": "license_keys",
"deliveryInstructions": "Paste the key under Help, Activate.",
"dynamicUrl": "",
"customFieldsOn": false,
"customFields": [],
"sort": 1,
"blockedPaymentMethods": []
}
],
"runningSubscriptions": 0
}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}{
"success": false,
"message": "<string>",
"code": "<string>",
"data": null,
"errors": {}
}Authorizations
A secret API key, made under Developers in the dashboard: Authorization: Bearer shoply_sk_...
Headers
A word of your own for this one change, up to 180 letters, digits, dots, dashes, underscores and colons. Sent again with the same key, the request is not carried out twice: the first answer comes back, with Idempotent-Replay: true.
Path Parameters
The store's ID. A key reaches the store it was made in: GET /key answers which.
The product.
Body
The product's name.
"Pro licence"
The product's options, 1 to 100 of them, in the order buyers see them. A variant of the product that is not in this list is removed.
Show child attributes
Show child attributes
The last part of its address on the storefront. Left out, it is made from the name.
What the product page says. Plain text with a few marks: a line that starts with # is a heading, one that starts with - is a point of a list, a blank line ends a paragraph, and **bold** and backticks for code work inside a line.
public is listed and can be bought. unlisted can be bought by whoever has its link. private cannot be bought.
public, unlisted, private Text sent to the buyer with every variant of the product once the order is paid, whatever the variant delivers, up to 5,000 characters. A variant's own instructions are sent after it. Empty is none. Left out on an update, it stays as it is.
The categories it is in. Left out on an update, they stay as they are.
Whether the storefront marks it as a best seller.
Up to 3 short labels on the product. Left out on an update, they stay as they are.
Show child attributes
Show child attributes
What the product does, as a list. Each entry is a feature on its own or a named group of features. Up to 500 features and 30 groups. Left out on an update, they stay as they are.
Show child attributes
Show child attributes
Payment methods the product cannot be bought with, by their names in the payment methods list. Left out on an update, they stay as they are.
What a payment method adds to this product's price: a payment method's name, and under it percent and fixed. A fee can only add. Left out on an update, the fees stay as they are.
{ "stripe": { "percent": 3, "fixed": 0.3 } }
The period the storefront's "sold" figure for this product counts.
all, 1h, 6h, 12h, 24h, 7d, 30d Anything of your own to keep with the product, as JSON. Shoply stores it and does nothing with it. It is answered as text.
The product's own title in search results, up to 70 characters. Empty is its name.
Its own description in search results, up to 200 characters. Empty is its description.
The updatedAt you last read. Sent, the change is refused with stale_write if the record has changed since. Left out, it is saved whatever happened in between.

