SHOP DESKDevelopers
shopdeskbd.com

Products

Products with their variants nested — variants are the commercial unit, since price, SKU and stock all hang off them.

Paste a key and every snippet on this page switches from the placeholder to your key — and the Try it panel under each endpoint is ready to send. It is stored in this browser only and goes nowhere except, if you press Send, straight to the API host you pick there.

List products

GET/ext/v1/productsproducts:read

Variants are nested in each product — they are the commercial unit. Use updated_after to sync only what changed.

Query parameters

NameTypeDescription
limitnumberdefault 25max 100Page size.
starting_afterstringReturn the page AFTER this object id (the previous page's next_cursor).
ending_beforestringReturn the page BEFORE this object id.
sortstringdefault -created_atNewest first by default. Only created_at is sortable — a cursor over a mutable key (like updated_at) cannot page reliably. Use updated_after to sync changes.created_at-created_at
store_idstringRestrict to one store. Required breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.
statusstringFilter to one of the values below.draftactivearchived
category_idstringOnly products in this category.
updated_afterdate-timeOnly products changed since this instant — the sync filter. Pair it with the default -created_at order and page with the cursor.
searchstringMatch on product name or variant SKU.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/products?limit=25&updated_after=2026-08-01T00%3A00%3A00Z" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
Try itruns from your browser
GEThttps://api.shopdeskbd.com/ext/v1/products

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Query parameters

Blank fields are left out of the request.

Paste a key above to enable Send.

Responses

  • 200A cursor page of products.
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "data": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "name": "Classic Cotton T-Shirt",
      "slug": "classic-cotton-t-shirt",
      "short_description": null,
      "description": null,
      "thumbnail": null,
      "status": "active",
      "delivery_charge": "1250.00",
      "rating_average": "4.50",
      "rating_count": 12,
      "brand": {
        "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
        "name": "Acme",
        "slug": "acme"
      },
      "categories": [
        {
          "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "name": "T-Shirts",
          "slug": "t-shirts",
          "is_primary": true
        }
      ],
      "images": [
        {
          "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "url": "https://cdn.example.com/p/1.webp",
          "position": 0,
          "alt_text": "Red cotton tee, front view",
          "is_thumbnail": true
        }
      ],
      "variants": [
        {
          "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "sku": "TSHIRT-RED-M",
          "barcode": "0123456789012",
          "name": "Red / M",
          "price": "1250.00",
          "discount_price": "1250.00",
          "weight": "0.25",
          "dimensions": null,
          "low_stock_threshold": 5,
          "status": "active",
          "image": {
            "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
            "url": "https://cdn.example.com/p/1.webp"
          },
          "options": [
            {
              "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
              "attribute": "Colour",
              "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
              "value": "Red"
            }
          ],
          "created_at": "2026-08-11T10:00:00.000Z",
          "updated_at": "2026-08-11T10:00:00.000Z"
        }
      ],
      "videos": [
        {
          "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
          "poster_url": "https://cdn.example.com/companies/c/videos/v/poster-1.webp",
          "width": 1080,
          "height": 1920,
          "duration_ms": 24000,
          "has_audio": true,
          "title": "Red saree drape",
          "gallery_slot": 1,
          "sort_order": 0,
          "option_value_id": "3c2b1a09-8f7e-4d6c-9b5a-4e3f2d1c0b9a"
        }
      ],
      "created_at": "2026-08-11T10:00:00.000Z",
      "updated_at": "2026-08-11T10:00:00.000Z"
    }
  ],
  "meta": {
    "has_more": true,
    "next_cursor": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001"
  }
}

Create a product

POST/ext/v1/productsproducts:write

At least one variant (a simple product has exactly one). Slugs auto-generate from the name and auto-suffix when taken; a duplicate SKU is a 409. A whole product can be built in this one call: photos by link in images[] (downloaded and stored before the call answers), each variant’s options[] by attribute and value name (create them first with POST /attributes), and each variant’s photo as image_index into images[]. One photo link that fails fails the whole call and nothing is written. Initial stock is not set here — it enters through POST /v1/inventory/adjustments. Image links: https only, PNG/JPEG/WebP/GIF read from the bytes (never SVG), at most 10 MB and 8000 px on the longest side (and 40 megapixels in all; 16 for a GIF), fetched within 10 s each and 30 s for the call, at most 50 MB downloaded per call. One failing link fails the whole call and nothing is written. Each image a call carries — link or file — counts as one write against the key's per-minute write limit; calls that download or upload images run at most 2 at a time per key; and a company may have 2,000 links downloaded per UTC day. Past any of these the answer is 429 RATE_LIMITED with Retry-After.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
namerequiredstring—
slugstringURL-safe slug. Auto-generated from the name when omitted; auto-suffixed when taken.
brand_iduuid—
short_descriptionstring—
descriptionstringRich description, stored as sanitized HTML. ProductsService — the same service the staff endpoints call — strips anything outside the allowlist, so an integration cannot store markup the admin UI could not. Plain text is accepted and kept as-is. An <img> is kept only when its src is on this platform’s own image origin (upload one through the admin POST /products/description-images); an image hosted anywhere else is removed, tag and all.
statusstringdefault draft—draftactivearchived
delivery_chargenumberPer-product delivery-charge override: 0 = free delivery, N = flat charge; omit for the standard shipping rules.
categoriesProductCategoryLinkDto[]—
variantsrequiredProductVariantWriteDto[]—
imagesProductImageWriteDto[]The gallery, in display order: up to 10 photos by source_url (send more with POST /products/{id}/images). The call returns once every photo is stored; one link that fails fails the whole call and nothing is written. Variants point at a photo with image_index.
videosProductVideoWriteDto[]E1c: videos to place on the new product — each a video THIS key (or a key it replaced by rotation) started (POST /videos or POST /videos/import), which may still be uploading or verifying; the product shows it once it is ready. Needs the videos:write scope as well as products:write, a plan with product videos, video uploads switched on, and no more than the plan's videos per product. Later changes go through POST /products/{id}/videos.

categories[] fields

NameTypeDescription
idrequireduuid—
is_primarybooleandefault false—

variants[] fields

NameTypeDescription
iduuidUpdate only: patches this existing variant. Omit to create a new one.
skurequiredstring—
barcodestring—
namerequiredstring—
pricerequirednumber—
discount_pricenumberSale price — must be strictly below price.
cost_pricenumberWhat the unit costs the store. Write-only (never serialized, Q6). Omitted on a new variant = 0; omitted on an existing variant (id sent) keeps its current cost. A changed cost on stock in hand is recorded as Stock revaluation on the P&L (only when the old and the new cost are both above 0).
weightnumber—
low_stock_thresholdnumberdefault 0—
statusstringdefault active—activeinactive
optionsVariantOptionWriteDto[]The options that define this variant, e.g. [{ "attribute": "Colour", "value": "Red" }, { "attribute": "Size", "value": "M" }]. Set when the variant is CREATED only (a product create, or an update entry without id); the options of an existing variant cannot be changed here, and an update keeps them. Two variants of a product cannot have the same combination.
image_indexintegerThis variant’s photo, as a zero-based position in THIS request’s images[] — the way to point at a photo sent in the same call. Null clears it. Use image_id instead for a photo the product already has.
image_iduuidUpdate only: this variant’s photo, as the id of one of the product’s existing photos. Null clears it. Not together with image_index.

images[] fields

NameTypeDescription
source_urlstringAn https link to a new photo. Shopdesk downloads it, checks the bytes (PNG, JPEG, WebP or GIF; at most 10 MB and 8000 px on the longest side) and stores a re-encoded copy — the link itself is not kept. A link that cannot be used fails the whole call with 422 naming images[i].source_url, and nothing is written.
image_iduuidUpdate only: an existing photo of this product (an id from images[]), kept in the gallery at this position.
alt_textstringScreen-reader alt text. On a kept photo, omit it to leave the stored text alone, or send null to clear it.
is_thumbnailbooleandefault falseMake this photo the product thumbnail (at most one entry). Without one, a new product takes its first photo, and an update keeps the current thumbnail if it stays in the gallery (else the first photo).

videos[] fields

NameTypeDescription
video_idrequireduuidA video THIS API key started (POST /videos or POST /videos/import). It may still be uploading or verifying: the product shows it once it is ready.
gallery_slotnumberdefault 1How many of the product's photos come before the video: 0 = before every photo, 1 (default) = right after the main photo. Clamped to the number of photos.
sort_ordernumberdefault 0Order among the product's videos; breaks ties within one slot.
option_value_iduuidShow the video for one option value (e.g. Colour: Red — the value id from GET /attributes, or a variant’s options[].value_id). A value one of the product's active variants uses; null or absent = every variant.
titlestringIts accessible name. Empty or null falls back to the product name.

options[] fields

NameTypeDescription
attributerequiredstringA variant attribute of this store: its id, slug or name (any case).
valuerequiredstringOne of that attribute’s values: its id or its text (any case).

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/products" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Classic Cotton T-Shirt",
  "status": "active",
  "variants": [
    {
      "sku": "TSHIRT-RED-M",
      "name": "Red / M",
      "price": 1250,
      "cost_price": 850
    }
  ]
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/products

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 201The created product, variants nested.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 409A variant SKU is already in use in this store, or two variants have the same options; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed: an unknown option name (variants[i].options[j].attribute / .value — nothing is created), a photo link that cannot be downloaded or is not an allowed image (images[i].source_url), or a variant photo that is not in images[].
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "name": "Classic Cotton T-Shirt",
  "slug": "classic-cotton-t-shirt",
  "short_description": null,
  "description": null,
  "thumbnail": null,
  "status": "active",
  "delivery_charge": "1250.00",
  "rating_average": "4.50",
  "rating_count": 12,
  "brand": {
    "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "name": "Acme",
    "slug": "acme"
  },
  "categories": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "name": "T-Shirts",
      "slug": "t-shirts",
      "is_primary": true
    }
  ],
  "images": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/p/1.webp",
      "position": 0,
      "alt_text": "Red cotton tee, front view",
      "is_thumbnail": true
    }
  ],
  "variants": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "sku": "TSHIRT-RED-M",
      "barcode": "0123456789012",
      "name": "Red / M",
      "price": "1250.00",
      "discount_price": "1250.00",
      "weight": "0.25",
      "dimensions": null,
      "low_stock_threshold": 5,
      "status": "active",
      "image": {
        "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
        "url": "https://cdn.example.com/p/1.webp"
      },
      "options": [
        {
          "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "attribute": "Colour",
          "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "value": "Red"
        }
      ],
      "created_at": "2026-08-11T10:00:00.000Z",
      "updated_at": "2026-08-11T10:00:00.000Z"
    }
  ],
  "videos": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
      "poster_url": "https://cdn.example.com/companies/c/videos/v/poster-1.webp",
      "width": 1080,
      "height": 1920,
      "duration_ms": 24000,
      "has_audio": true,
      "title": "Red saree drape",
      "gallery_slot": 1,
      "sort_order": 0,
      "option_value_id": "3c2b1a09-8f7e-4d6c-9b5a-4e3f2d1c0b9a"
    }
  ],
  "created_at": "2026-08-11T10:00:00.000Z",
  "updated_at": "2026-08-11T10:00:00.000Z"
}

Get a product by id

GET/ext/v1/products/{id}products:read

The full product object, variants nested.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
Try itruns from your browser
GEThttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Paste a key above to enable Send.

Responses

  • 200The product.
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "name": "Classic Cotton T-Shirt",
  "slug": "classic-cotton-t-shirt",
  "short_description": null,
  "description": null,
  "thumbnail": null,
  "status": "active",
  "delivery_charge": "1250.00",
  "rating_average": "4.50",
  "rating_count": 12,
  "brand": {
    "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "name": "Acme",
    "slug": "acme"
  },
  "categories": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "name": "T-Shirts",
      "slug": "t-shirts",
      "is_primary": true
    }
  ],
  "images": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/p/1.webp",
      "position": 0,
      "alt_text": "Red cotton tee, front view",
      "is_thumbnail": true
    }
  ],
  "variants": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "sku": "TSHIRT-RED-M",
      "barcode": "0123456789012",
      "name": "Red / M",
      "price": "1250.00",
      "discount_price": "1250.00",
      "weight": "0.25",
      "dimensions": null,
      "low_stock_threshold": 5,
      "status": "active",
      "image": {
        "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
        "url": "https://cdn.example.com/p/1.webp"
      },
      "options": [
        {
          "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "attribute": "Colour",
          "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "value": "Red"
        }
      ],
      "created_at": "2026-08-11T10:00:00.000Z",
      "updated_at": "2026-08-11T10:00:00.000Z"
    }
  ],
  "videos": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
      "poster_url": "https://cdn.example.com/companies/c/videos/v/poster-1.webp",
      "width": 1080,
      "height": 1920,
      "duration_ms": 24000,
      "has_audio": true,
      "title": "Red saree drape",
      "gallery_slot": 1,
      "sort_order": 0,
      "option_value_id": "3c2b1a09-8f7e-4d6c-9b5a-4e3f2d1c0b9a"
    }
  ],
  "created_at": "2026-08-11T10:00:00.000Z",
  "updated_at": "2026-08-11T10:00:00.000Z"
}

Update a product

PATCH/ext/v1/products/{id}products:write

Partial update of the product fields. categories (when present) replaces the link set; variants (when present) reconciles the FULL variant set — patch by id, create without one, delete the rest unless stock history forbids it. Same semantics as the dashboard. A variant keeps its options; options[] is taken on NEW entries only. images (when present) is the FULL gallery: image_id entries keep those photos, source_url entries add new ones, and photos left out are deleted — to only add photos, use POST /products/{id}/images. Image links: https only, PNG/JPEG/WebP/GIF read from the bytes (never SVG), at most 10 MB and 8000 px on the longest side (and 40 megapixels in all; 16 for a GIF), fetched within 10 s each and 30 s for the call, at most 50 MB downloaded per call. One failing link fails the whole call and nothing is written. Each image a call carries — link or file — counts as one write against the key's per-minute write limit; calls that download or upload images run at most 2 at a time per key; and a company may have 2,000 links downloaded per UTC day. Past any of these the answer is 429 RATE_LIMITED with Retry-After.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
namestring—
slugstring—
brand_iduuidSet null to detach the brand.
short_descriptionstring—
descriptionstring—
statusstring—draftactivearchived
delivery_chargeobjectSet null to return to the standard shipping rules.
categoriesProductCategoryLinkDto[]When present, REPLACES the product's category links.
variantsProductVariantWriteDto[]When present, reconciles the FULL variant set in one transaction — entries with an id are patched, entries without one are created, and existing variants missing from the list are removed (refused when they carry stock history). Same semantics as the dashboard.
imagesProductImageWriteDto[]When present, the FULL gallery in its new order, like variants: entries with image_id keep that photo, entries with source_url add a new one (at most 10 per call), and photos missing from the list are DELETED — variants that showed them lose the link. [] empties the gallery. At most 30 photos. To add photos without restating the gallery, use POST /products/{id}/images.

categories[] fields

NameTypeDescription
idrequireduuid—
is_primarybooleandefault false—

variants[] fields

NameTypeDescription
iduuidUpdate only: patches this existing variant. Omit to create a new one.
skurequiredstring—
barcodestring—
namerequiredstring—
pricerequirednumber—
discount_pricenumberSale price — must be strictly below price.
cost_pricenumberWhat the unit costs the store. Write-only (never serialized, Q6). Omitted on a new variant = 0; omitted on an existing variant (id sent) keeps its current cost. A changed cost on stock in hand is recorded as Stock revaluation on the P&L (only when the old and the new cost are both above 0).
weightnumber—
low_stock_thresholdnumberdefault 0—
statusstringdefault active—activeinactive
optionsVariantOptionWriteDto[]The options that define this variant, e.g. [{ "attribute": "Colour", "value": "Red" }, { "attribute": "Size", "value": "M" }]. Set when the variant is CREATED only (a product create, or an update entry without id); the options of an existing variant cannot be changed here, and an update keeps them. Two variants of a product cannot have the same combination.
image_indexintegerThis variant’s photo, as a zero-based position in THIS request’s images[] — the way to point at a photo sent in the same call. Null clears it. Use image_id instead for a photo the product already has.
image_iduuidUpdate only: this variant’s photo, as the id of one of the product’s existing photos. Null clears it. Not together with image_index.

images[] fields

NameTypeDescription
source_urlstringAn https link to a new photo. Shopdesk downloads it, checks the bytes (PNG, JPEG, WebP or GIF; at most 10 MB and 8000 px on the longest side) and stores a re-encoded copy — the link itself is not kept. A link that cannot be used fails the whole call with 422 naming images[i].source_url, and nothing is written.
image_iduuidUpdate only: an existing photo of this product (an id from images[]), kept in the gallery at this position.
alt_textstringScreen-reader alt text. On a kept photo, omit it to leave the stored text alone, or send null to clear it.
is_thumbnailbooleandefault falseMake this photo the product thumbnail (at most one entry). Without one, a new product takes its first photo, and an update keeps the current thumbnail if it stays in the gallery (else the first photo).

options[] fields

NameTypeDescription
attributerequiredstringA variant attribute of this store: its id, slug or name (any case).
valuerequiredstringOne of that attribute’s values: its id or its text (any case).

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X PATCH "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "active"
}'
Try itruns from your browser
PATCHhttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The updated product, variants nested.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found.
  • 409A variant SKU is already in use in this store, or two variants have the same options; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed: options sent on an existing variant, an unknown option name, an image_id that is not a photo of this product, or a photo link that cannot be downloaded or is not an allowed image — error.fields names the field.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "name": "Classic Cotton T-Shirt",
  "slug": "classic-cotton-t-shirt",
  "short_description": null,
  "description": null,
  "thumbnail": null,
  "status": "active",
  "delivery_charge": "1250.00",
  "rating_average": "4.50",
  "rating_count": 12,
  "brand": {
    "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "name": "Acme",
    "slug": "acme"
  },
  "categories": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "name": "T-Shirts",
      "slug": "t-shirts",
      "is_primary": true
    }
  ],
  "images": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/p/1.webp",
      "position": 0,
      "alt_text": "Red cotton tee, front view",
      "is_thumbnail": true
    }
  ],
  "variants": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "sku": "TSHIRT-RED-M",
      "barcode": "0123456789012",
      "name": "Red / M",
      "price": "1250.00",
      "discount_price": "1250.00",
      "weight": "0.25",
      "dimensions": null,
      "low_stock_threshold": 5,
      "status": "active",
      "image": {
        "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
        "url": "https://cdn.example.com/p/1.webp"
      },
      "options": [
        {
          "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "attribute": "Colour",
          "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
          "value": "Red"
        }
      ],
      "created_at": "2026-08-11T10:00:00.000Z",
      "updated_at": "2026-08-11T10:00:00.000Z"
    }
  ],
  "videos": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
      "poster_url": "https://cdn.example.com/companies/c/videos/v/poster-1.webp",
      "width": 1080,
      "height": 1920,
      "duration_ms": 24000,
      "has_audio": true,
      "title": "Red saree drape",
      "gallery_slot": 1,
      "sort_order": 0,
      "option_value_id": "3c2b1a09-8f7e-4d6c-9b5a-4e3f2d1c0b9a"
    }
  ],
  "created_at": "2026-08-11T10:00:00.000Z",
  "updated_at": "2026-08-11T10:00:00.000Z"
}

Archive a product

POST/ext/v1/products/{id}/archiveproducts:write

Non-destructive: variants, images and inventory survive with their status parked, and the dashboard can restore. Archived products drop out of GET /v1/products.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/archive" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/archive

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 201Archival confirmed.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "archived": true
}

Add product images

POST/ext/v1/products/{id}/imagesproducts:write

Adds photos to the product gallery, in one of three ways. **One file:** multipart/form-data with the file in the image part and its options (alt_text, position, is_thumbnail) as text fields; the answer is that image. **Several files:** up to 10 image parts (50 MB in all), with per-file options as a JSON array in one images text field (one entry per part, same order). **Photos by link:** a JSON body { "images": [{ "source_url": "https://…", "alt_text": "…" }] } with up to 10 links, which Shopdesk downloads and stores (the links themselves are not kept). A batch (anything with images, or several parts) answers { "data": [...] }: the new images in the order sent, with their ids. It is all or nothing — one refused file or link fails the call and nothing is added. Images are appended unless position says otherwise; is_thumbnail: true makes one the product thumbnail, and a product with no images yet takes its first image as the thumbnail anyway. A gallery holds at most 30 images. Every file is read from its bytes: PNG, JPEG, WebP or GIF, at most 10 MB and 8000 px on the longest side. Image links: https only, PNG/JPEG/WebP/GIF read from the bytes (never SVG), at most 10 MB and 8000 px on the longest side (and 40 megapixels in all; 16 for a GIF), fetched within 10 s each and 30 s for the call, at most 50 MB downloaded per call. One failing link fails the whole call and nothing is written. Each image a call carries — link or file — counts as one write against the key's per-minute write limit; calls that download or upload images run at most 2 at a time per key; and a company may have 2,000 links downloaded per UTC day. Past any of these the answer is 429 RATE_LIMITED with Retry-After. Idempotency-Key is honoured here as on every write: on a JSON body the fingerprint covers the links, and a replay answers without downloading again; on a multipart request it covers the route only — reuse a key only when retrying the same files.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
imagesrequiredProductImageUploadEntryDto[]Up to 10 photos, each with the source_url to download, in the order to add them. The call answers { data: [...] }: the new photos in the order sent.

images[] fields

NameTypeDescription
source_urlstringJSON body: the https link to download (required). Not allowed in a multipart form, where the entry describes the image part at its index.
alt_textstringScreen-reader alt text for this photo.
positionintegerZero-based position in the gallery. Existing images shift to make room; a value past the end appends. Applied in list order, as if each photo were uploaded on its own; omit to append.
is_thumbnailbooleandefault falseMake this photo the product thumbnail (at most one entry).

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/images" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "images": [
    {
      "source_url": "https://cdn.example.com/tees/red-front.jpg",
      "alt_text": "Red cotton tee, front view",
      "is_thumbnail": true
    }
  ]
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/images

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 201A single upload: the stored image, as it appears in images[]. A batch: { data }, the new images in the order sent.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found, or the image is not on this product.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 413A file is larger than 10 MB, or the files of the request total more than 50 MB.
  • 422No image at all, a file or link that is not a readable PNG/JPEG/WebP/GIF (a link that cannot be downloaded is named as images[i].source_url), an invalid text field, or a gallery that would pass 30 images — error.fields names the field.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "url": "https://cdn.example.com/p/1.webp",
  "position": 0,
  "alt_text": "Red cotton tee, front view",
  "is_thumbnail": true
}

Update a product image

PATCH/ext/v1/products/{id}/images/{imageId}products:write

Changes the alt text, the gallery position and/or which image is the product thumbnail. The bytes cannot be replaced — upload a new image and delete the old one. Fields left out are left alone.

Path parameters

NameTypeDescription
idrequireduuidThe product id.
imageIdrequireduuidThe image id, as listed in the product’s images[].

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
alt_textobjectScreen-reader alt text. Set null to clear it.
positionintegerZero-based position in the gallery. Existing images shift to make room; a value past the end appends.
is_thumbnailbooleantrue makes this image the product thumbnail. false on the current thumbnail hands the role to the first image in the gallery.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X PATCH "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/images/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "alt_text": "Red cotton tee, front view",
  "position": 0
}'
Try itruns from your browser
PATCHhttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/images/9c1e4b2a-7d3f-4e58-b0a1-2f6d8c9e1a45

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The updated image, as it appears in images[].
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found, or the image is not on this product.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "url": "https://cdn.example.com/p/1.webp",
  "position": 0,
  "alt_text": "Red cotton tee, front view",
  "is_thumbnail": true
}

Delete a product image

DELETE/ext/v1/products/{id}/images/{imageId}products:write

Removes the image from the gallery and from storage — this is permanent, unlike archiving a product. Variants that featured it lose the link; if it was the thumbnail, the first remaining image takes over (or thumbnail becomes null). A company-scoped key names the store with the store_id query parameter, since a DELETE carries no body.

Path parameters

NameTypeDescription
idrequireduuidThe product id.
imageIdrequireduuidThe image id, as listed in the product’s images[].

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X DELETE "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/images/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47"
Try itruns from your browser
DELETEhttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/images/9c1e4b2a-7d3f-4e58-b0a1-2f6d8c9e1a45

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Empty
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200Deletion confirmed.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found, or the image is not on this product.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "deleted": true
}

Reorder product images

POST/ext/v1/products/{id}/images/reorderproducts:write

Puts the whole gallery in a new order: image_ids lists every image of the product exactly once, in the order to show them. A list that leaves an image out or names one twice is refused, so a reorder never removes a photo (delete one with its own DELETE). The thumbnail and alt texts stay as they are. Answers the gallery in its new order.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
image_idsrequiredstring[]Every image id of the product, each once, in the new display order. A list that leaves one out or repeats one is refused, so a reorder never removes a photo.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/images/reorder" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "image_ids": [
    "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "9c1e4b2a-7d3f-4e58-b0a1-2f6d8c9e1a45"
  ]
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/images/reorder

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The gallery in its new order.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Product not found.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422image_ids is not exactly the product’s images — one is missing, repeated or not on this product (error.fields.image_ids).
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "data": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "url": "https://cdn.example.com/p/1.webp",
      "position": 0,
      "alt_text": "Red cotton tee, front view",
      "is_thumbnail": true
    }
  ]
}

Get a variant by id, SKU or barcode

GET/ext/v1/variants/{identifier}products:read

Answers "what is this thing in my hand?" for a scanned barcode or a SKU from a spreadsheet.

Path parameters

NameTypeDescription
identifierrequiredstringA variant id, SKU or barcode. All three resolve here.

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/variants/SKU-001" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
Try itruns from your browser
GEThttps://api.shopdeskbd.com/ext/v1/variants/TSHIRT-RED-M

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Paste a key above to enable Send.

Responses

  • 200The variant.
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Variant not found.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "sku": "TSHIRT-RED-M",
  "barcode": "0123456789012",
  "name": "Red / M",
  "price": "1250.00",
  "discount_price": "1250.00",
  "weight": "0.25",
  "dimensions": null,
  "low_stock_threshold": 5,
  "status": "active",
  "image": {
    "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "url": "https://cdn.example.com/p/1.webp"
  },
  "options": [
    {
      "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "attribute": "Colour",
      "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "value": "Red"
    }
  ],
  "created_at": "2026-08-11T10:00:00.000Z",
  "updated_at": "2026-08-11T10:00:00.000Z"
}

Update a variant

PATCH/ext/v1/variants/{identifier}products:write

Changes one variant’s price, discount_price, sku, barcode, weight or photo (image_id) and leaves the rest alone — the narrow edit beside PATCH /products/{id}, which restates the whole variant set. null clears the sale price, barcode, weight or photo. The same rules as the dashboard: a sale price must stay below the price, and the SKU is locked once the variant has stock or order history. Emits product.updated. Answers the variant as GET /variants/{identifier} does.

Path parameters

NameTypeDescription
identifierrequiredstringThe variant id, SKU or barcode — tried in that order, each as an exact match, inside the write store.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
pricenumber—
discount_pricenumberSale price — must be strictly below the price (the new one, or the stored one when price is not sent). Null removes the sale price.
skustringA new SKU, unique in the store. Refused (409) once the variant has stock or order history.
barcodestringNull removes the barcode.
weightnumberShipping weight in kg. Null removes it.
image_iduuidThis variant’s photo: the id of one of the product’s photos (from images[]). Null clears it, and the storefront falls back to the product thumbnail.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X PATCH "https://api.shopdeskbd.com/ext/v1/variants/SKU-001" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "price": 1350,
  "discount_price": 1199
}'
Try itruns from your browser
PATCHhttps://api.shopdeskbd.com/ext/v1/variants/TSHIRT-RED-M

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The updated variant.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403The key's scopes do not cover this endpoint.
  • 404Variant not found (or its product is archived).
  • 409The SKU is taken by another variant, or locked by stock or order history; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "sku": "TSHIRT-RED-M",
  "barcode": "0123456789012",
  "name": "Red / M",
  "price": "1250.00",
  "discount_price": "1250.00",
  "weight": "0.25",
  "dimensions": null,
  "low_stock_threshold": 5,
  "status": "active",
  "image": {
    "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
    "url": "https://cdn.example.com/p/1.webp"
  },
  "options": [
    {
      "attribute_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "attribute": "Colour",
      "value_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "value": "Red"
    }
  ],
  "created_at": "2026-08-11T10:00:00.000Z",
  "updated_at": "2026-08-11T10:00:00.000Z"
}