Skip to content

Write a whole product in one request

A product on the OLET API is one document. POST /products and PATCH /products/{id} accept the same nested document that GET /products/{id} returns, so you can read a product, change it, and send it straight back. Everything in the request is saved together, or nothing is.

Besides its own fields, a product carries five collections:

  • codes: barcodes, each {type, code}. type is one of upc, isbn, itf, ean, jan or custom.
  • tags: tags attached to the product, each {id} where id is the tag’s own id. A read also shows the tag name.
  • suppliers: supplier links, each {supplier_id, is_primary, supplier_code}. A read also shows supplier_name.
  • variant_attributes: the attributes of a variant parent, each {name, position} with position 1 to 3.
  • components: the products inside a composite product, each {product_id, quantity}.

Only name and retail_price are required to create a product. retail_price is an integer number of cents. See money and quantities.

How do I create a product with its collections

Section titled “How do I create a product with its collections”

Send the collections in the same request.

Terminal window
curl -X POST https://api.oletapp.com/your-store/v1/products \
-H "Authorization: Bearer olet_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Cold brew concentrate",
"retail_price": 1299,
"codes": [{"type": "ean", "code": "4006381333931"}],
"tags": [{"id": "TAG_ID"}],
"suppliers": [{"supplier_id": "SUPPLIER_ID", "is_primary": true}]
}'

You need the catalog:write scope. If an entry in any collection is invalid, nothing is created, not even the product.

PATCH /products/{id} changes only the fields you send. The rule for each collection is simple:

  • Leave the key out and that collection is untouched.
  • Send an array and it replaces the collection. Items are matched by what they are (a code’s type, a tag’s id, a supplier’s id, a component’s product, an attribute’s position), so a row you re-send keeps its identity.
  • Send [] and the collection is cleared.
const url = 'https://api.oletapp.com/your-store/v1/products/PRODUCT_ID'
const headers = {
Authorization: 'Bearer olet_live_YOUR_API_KEY',
'Content-Type': 'application/json',
}
const { data: product } = await (await fetch(url, { headers })).json()
product.retail_price = 1399
await fetch(url, { method: 'PATCH', headers, body: JSON.stringify(product) })

Fields the API owns, such as id, created_at, updated_at, image_thumb_url, is_gift_card and variant_options, are accepted and ignored, which is why the read document goes straight back. A sku in a PATCH is ignored too, so a SKU is set only when you create the product. Leave sku out of a create and OLET generates one that starts with PRD-.

How do I avoid overwriting someone else’s change

Section titled “How do I avoid overwriting someone else’s change”

Send the version you read back with your update. If the product changed in the meantime, you get 409 STALE_VERSION: reload it and apply your change again. expected_version works the same way; if you send both, they must agree.

A product can have up to 10 codes, 20 tags, 10 suppliers and 50 components, and up to 3 variant attributes. Going over a cap answers 422 UNPROCESSABLE, except variant attributes, where a fourth attribute or a position outside 1 to 3 answers 400 VALIDATION_FAILED. variant_attributes can only change while the product has no live variant children; after that, a change answers 409 CONFLICT, but sending exactly what is already there is accepted.

A tag id that does not exist is a 400. A SKU already in use is a 409 CONFLICT. On a store at its product limit, a create answers 402 PRODUCT_LIMIT_REACHED. See errors.

Send image_url with an https address. OLET downloads the image once and stores its own copy; the address you sent is never kept. If the download fails, the product is still saved and the response is 422 IMAGE_FETCH_FAILED with the saved product in data. There is no way to remove an image through the API: null and an omitted image_url both leave the image as it is. Image downloads have their own limit, see rate limits.

Create the parent with has_variants set to true and its variant_attributes, for example Size at position 1 and Color at position 2. Then add the children with POST /products/{id}/variants. Each child names its own combination of options, as attribute_id and value pairs, and may carry its own name, sku, retail_price and active.

Terminal window
curl -X POST https://api.oletapp.com/your-store/v1/products/PARENT_ID/variants \
-H "Authorization: Bearer olet_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"variants": [
{"options": [{"attribute_id": "SIZE_ATTRIBUTE_ID", "value": "M"}, {"attribute_id": "COLOR_ATTRIBUTE_ID", "value": "Black"}]},
{"options": [{"attribute_id": "SIZE_ATTRIBUTE_ID", "value": "L"}, {"attribute_id": "COLOR_ATTRIBUTE_ID", "value": "Black"}]}
]}'

You can send up to 200 variants, and the request is all or nothing. A combination that already exists answers 409 CONFLICT. The children are ordinary products from then on: read, change and delete them with /products/{id}. The call accepts an Idempotency-Key, see idempotency.

See delete and erase for removing products and customers, or the full API reference for every product field.