No route deletes a product: set
active: false to take one off the store.
Stock is its own resource (Stock), and so is the buy price
(read_costs).
The product
typeisPHYSICALorPACK(a bundle of other products); an open string.activeis false once the product is taken off the store.- Every product has a default variant (
isDefault) that follows the product’s own name, image, SKU, barcode and prices. A product with options has one more variant per combination of option values. - Deleted variants and options are left out.
List and read
GET /products lists every product of your installation’s stores, active
or not, newest first. Filters: storeId, collectionId (the collection’s
members), active (true or false), createdSince, updatedSince. A
collection membership change does not move updatedAt: listen to
products/updated, which names collectionIds.
Images on the storefront
A product’s images and description are loaded by every buyer’s browser on the store, the page that holds the order form. So an app may only write images that are:- on the Tassyir CDN:
https://cdn.tassyir.io/…; or - already shown by the same product or collection (its image, gallery, variant images or description images), so rewriting a product keeps the merchant’s own images.
MEDIA_URL_NOT_ALLOWED (field, url).
A description is HTML: paragraphs, headings, lists, emphasis, links and
CDN images. Refused with 400 HTML_NOT_ALLOWED (field, problems):
<script>, <style>, <link>, <meta>, <base>, frames, <object>,
<embed>, forms and their fields, <template>, any on… attribute,
srcdoc, ping, links that are not http, https, mailto or tel (or
that start with //), and inline CSS that can load something (url(,
@import, image-set(). null or "" clears a description.
Bring an image onto the CDN
An image from elsewhere (a supplier’s feed, another platform) goes throughPOST /images first. Tassyir downloads it, keeps it only if it is a JPEG,
PNG, GIF or WebP, stores it on its CDN in the merchant’s media library, and
answers the URL the catalogue writes accept.
url as it is in a product’s image or images, a variant’s image, a
collection’s image, or an <img src> of a description. id is the image
in the merchant’s media library, where it shows your app as its source. An
image already on https://cdn.tassyir.io/ needs no upload.
Limits
A download that was attempted counts, whatever it found. Send an
Idempotency-Key per image: a retry with the same key answers the same image
without downloading or counting it again.
@tassyir/app never retries 429 IMAGE_QUOTA_REACHED, even with retries
on: the error comes back at once, and its problem says which quota is full
(per, window: day or minute, limit, retryAfter), so your app can
wait out a minute window or stop for the day.
A raw file upload (multipart) is not offered yet: host the file somewhere
first.
Create a product
Any other field (
tracksInventory, type, tags, …) is 400
VALIDATION_FAILED. The option and variant rules refuse with their own codes
(PRODUCT_VARIANT_COUNT_MISMATCH, PRODUCT_VARIANT_MISSING_COMBINATIONS,
PRODUCT_OPTION_DUPLICATE_VALUE, …) and details. The answer is 201 with the
product read again, and products/created is sent.
Change a product
PATCH /products/{id} saves the fields you send (the create’s, but not
storeId or collectionIds); null clears the nullable ones.
optionsandvariantsreplace the product’s, as the dashboard’s editor saves them: sendoptionswith every value andvariantswith every combination. A variant with anidkeeps that variant and changes only the fields you send; one withoutidis new and needsprice; a variant left out is removed.variantsalone keeps the options.- A product may keep the outside images it already shows.
PATCH /products/{id}/variants/{variantId} with any of name, sku,
barcode, price, compareAtPrice, image, hidden:
PRODUCT_VARIANT_COUNT_MISMATCH: read the product again and retry. The default
variant follows its product: 409 PRODUCT_DEFAULT_VARIANT, change the
product instead.
Collections
A collection holds the products picked by hand, plus those matching its conditions, minus those excluded by hand. Its page lists them inproductSort order.
parentIdnests a collection under another of the same store.productSort:UPDATED,NEWEST,OLDEST,PRICE_ASC,PRICE_DESC,NAME_ASC,NAME_DESCorMANUAL(an open string).- Conditions:
ruleMatch(ALLorANY,nullwithout rules) and up to 10rules:fieldBRANDorTAG(withvalueRefIds),PRICE(valueNumber, andvalueNumberMaxforBETWEEN, in dinars),ON_SALE,TITLE(valueText);operatorIS,IS_NOT,LESS_THAN,MORE_THAN,BETWEEN,CONTAINS,NOT_CONTAINS,STARTS_WITH,IS_TRUE,IS_FALSE. - To list its products:
GET /products?collectionId=<id>.
PATCH /collections/{id}/products:addpicks products by hand (and lifts an exclusion),removetakes them out whatever put them there. Up to 250 ids each, products of the collection’s store. Each product that entered or left sendsproducts/updatedwithcollectionIds.DELETE /collections/{id}: its products stay in the catalogue, and its subcollections move up one level.- Collections send no webhook of their own.
Errors these routes add
The catalogue’s own codes (options, variants, collections, brands) reach you
unchanged, with their details.