Set the customisation groups on one listing
- Credentials
- OAuth access token
- Scopes
listings:write- Subscription
- Needs an active plan. Otherwise
403 subscription_locked. - Idempotency
- Send an
Idempotency-Keyheader. A retry with the same key does the work once.
The groups this listing offers, in order, each with its own prices: prices maps a choice id to a price, variantPrices maps a variant (or ticket tier) id and then a choice id. An override that repeats the inherited price, or names something the listing no longer has, is dropped. An empty list takes every group off. getProduct returns the same shape in customisations.
Access: OAuth scope listings:write; write-gated by the subscription.
merchantOAuth listings:writeAuthorizationBearer <token>OAuth 2.1 (authorization code + PKCE S256, public clients, dynamic registration) for connected AI apps. Effective power is scopes granted AND the person's role today AND the plan. orders:refund, billing:write and team:write are opt-in on the consent screen. Write implies read for marketing:read and messages:read, so a grant holding marketing:write passes a marketing:read check, and messages:send passes messages:read.
Scope: listings:write
storeId*stringA store the person can reach. Any other id answers 404, never 403.
productId*stringAn item id (any kind, not only physical products).
Idempotency-Key*stringRequired on every write. Minted on the device when the person taps, reused on every retry. A write without one, or with one that does not match the pattern, is refused with 422 validation_error. Scoped to the token's subject, remembered 24 hours. A replay returns the first 2xx response with Idempotent-Replay: true; a request still running answers 409 in_flight; the same key with a different method, path or body answers 409 idempotency_key_reused; non-2xx results are not remembered. If the key store cannot be read nothing is processed and the answer is 503 idempotency_unavailable: retry with the same key.
^[A-Za-z0-9_-]{8,128}$application/json- body
customisations*array<>OK
application/json- response
data*curl -X PUT 'https://api.shopeazy.co/v1/merchant/stores/<storeId>/products/<productId>/customisations' \ -H 'Authorization: Bearer <access_token>' \ -H 'Idempotency-Key: <unique_key>' \ -H 'Content-Type: application/json' \ -d '{ "customisations": [ { "groupId": "QdChQIpRuzJvj6uu3Huc" } ]}'{ "data": { "productId": "65myAK8IFZKwZBG84Gmw", "customisations": [ { "id": "QdChQIpRuzJvj6uu3Huc", "name": "Gift wrapping", "description": "Choose how it arrives. Cards are written by hand.", "select": "one", "required": false, "max": null, "status": "active", "sortOrder": 0, "choices": [ { "id": "f0e14318", "name": "Plain brown paper", "price": { "amount": 0, "currency": "ZAR" }, "available": true, "description": null, "imageUrl": null }, { "id": "a26f9c74", "name": "Ribbon and card", "price": { "amount": 4500, "currency": "ZAR" }, "available": true, "description": "A handwritten card in the box.", "imageUrl": null } ], "updatedAt": 1792924389056, "groupId": "QdChQIpRuzJvj6uu3Huc", "prices": {}, "variantPrices": {} } ] }}Offer a group on exactly these listings PUT
The group is added where it is missing (at the end of that listing's groups) and taken off every other listing. A listing that keeps it keeps its price overrides. Any kind of listing, archived ones excepted. Access: OAuth scope `listings:write`; write-gated by the subscription.
Past product imports, newest first GET
Runs started through the API and the console's imports; the newest 200 are listed. Access: OAuth scope `listings:read`; not write-gated. Paged: `{ items, nextCursor }`, `limit` (default 20, at most 50) and an opaque `cursor`.