Update product and append vouchers
Replace one Store product's settings with a full body, and add any new voucher codes to its stock.
- Method
- PUT
- Path
https://api.returning.ai / v1/ products/ {productID} - Permission
- store
- Retries
- Repeats change nothing; re-sent codes get 409
When to use this
- A product is running low and you have new voucher codes to add.
- You change a product's price, image, category or access rules.
- You archive a product so traders no longer see it, or bring it back.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- storeShown in the dashboard as “Store”
Send a Community API key with the store permission. The key decides the community, so never send a community ID. Keep the key on your server.
Behaviour#
The codes in vouchers are checked first: if the product already has any of them, nothing changes. Then the product's settings are replaced and the new codes are added as stock. Existing codes are never removed or replaced.
Setting isArchived to true takes the product out of List products and your Store; Read product still returns it. productPermission: [] removes every access rule and sets isProductAccessEnabled to false.
Request#
Path parameters#
Rule24-character hex ID
Eg66f000000000000000000101
Headers#
Body#
This is a full replace, not a patch: send every field except expiringVoucherHighlight, which you can omit to keep. Start from Read product: send category back as categoryName and purchaseStatusOverride.status as purchaseStatusOverrideName. To change settings without adding stock, send vouchers: [] and voucherExpireDate: "".
Rule1-100 characters
Eg"$25 Trading Credit"
p, br, strong, b, em, i, u, span and a tags are kept. Send "" for none.RuleString, may be empty
Eg"<p>Redeem 500 coins for a $25 trading credit on your live account.</p>"
null for no image.Rulehttps:// URL or null
Eg"https://cdn.example.com/store/trading-credit-25.png"
RuleNumber, 0 or more
Eg500
null removes the category.RuleExisting category name or null
Eg"Trading rewards"
409. Send [] to add none.RuleArray of non-empty strings
Eg["TC25-8R5N-3C1Y"]
vouchers. Required when vouchers has codes; send "" when it is empty. Codes that have already expired are saved but don't count as stock.RuleISO 8601 date, or ""
Eg"2026-12-31T23:59:59.000Z"
true archives the product: it leaves List products and the Store. false shows it again.Egfalse
null.RuleString or null
Eg"<p>Your credit is added to your trading account after approval.</p>"
isDiscountEnabled is true. Send 0 when there is no discount.Rule0 or more; below price when the discount is on
Eg0
Ruletrue needs both discount dates
Egfalse
null.RuleISO 8601 date or null; before discountEndDate
Egnull
null.RuleISO 8601 date or null; after discountStartDate
Egnull
Eg{ ... }
true to ask for details.Egfalse
Egfalse
Egfalse
Egfalse
true to show traders how many are left.Egtrue
true to prioritise the vouchers closest to expiry.Egfalse
Eg{"isEnabled": false, "duration": 7, "timeframe": "days"}
true to turn the highlight on.Egfalse
timeframe units.RuleWhole number, 0 or more
Eg7
duration, such as days.RuleString
Eg"days"
true to apply the rules in productPermission; false to follow the category. Saved as false when productPermission is empty.Egfalse
[] removes them all. Names are matched in your community.RuleArray
Eg[]
true when the rule is active.Egtrue
Ruleuser, role, tag or role_combination
Eg"role"
[] for none.RuleExisting role names
[] for none.RuleExisting tag names
400 with Unable to resolve usernames: <names>. [] for none.RuleExisting usernames; case-sensitive
[] for none.values are role or tag names.Rulerole or tag
Eg"role"
true when the rule covers all traders. Required whenever you send permission.Egfalse
Rule0 or more
Eg80
true to charge the special price.Egfalse
true to apply the limit.Egfalse
RuleWhole number, 0 or more
Eg1
intervalUnits.RuleWhole number, 1 or more
Eg1
Ruledays, weeks, months or years
Eg"months"
null means full access.Rulefull-access, view-only, access-denied or null
Eg"full-access"
true to show the exclusive tag.Egfalse
null to use the category or community default.RuleExisting status name or null
Egnull
curl --request PUT \
--url https://api.returning.ai/v1/products/66f000000000000000000101 \
--header 'Authorization: Bearer <API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"name": "$25 Trading Credit",
"description": "<p>Redeem 500 coins for a $25 trading credit on your live account.</p>",
"image": "https://cdn.example.com/store/trading-credit-25.png",
"price": 500,
"categoryName": "Trading rewards",
"vouchers": ["TC25-8R5N-3C1Y"],
"voucherExpireDate": "2026-12-31T23:59:59.000Z",
"isArchived": false,
"redemptionInstructions": "<p>Your credit is added to your trading account after approval.</p>",
"discountPrice": 0,
"isDiscountEnabled": false,
"discountStartDate": null,
"discountEndDate": null,
"userInformation": {
"isEnabled": false,
"shouldCollectName": false,
"shouldCollectPhone": false,
"shouldCollectAddress": false
},
"shouldDisplayRemainingQuantity": true,
"shouldPrioritiesExpiringVouchers": false,
"expiringVoucherHighlight": {
"isEnabled": false,
"duration": 7,
"timeframe": "days"
},
"isProductAccessEnabled": false,
"productPermission": [],
"purchaseStatusOverrideName": null
}'
Response#
A 200 returns the product after the update in data. stocks now includes the codes you added, unless they have already expired; voucher codes are never returned. Branch on the HTTP status and meta.code, never on message.
Eg{ ... }
Rulesuccess
Eg200
RulePRODUCT_UPDATED
Eg"Update product success."
Eg{ ... }
Eg"66f000000000000000000101"
Eg"$25 Trading Credit"
Eg500
null when the product has no category.null when the product has no category or its category was deleted.Eg"Trading rewards"
Eg2
true when the product is archived and left out of List products and the Store.Egfalse
null when there is no image.Eg"https://cdn.example.com/store/trading-credit-25.png"
Eg"<p>Redeem 500 coins for a $25 trading credit on your live account.</p>"
null.Eg"<p>Your credit is added to your trading account after approval.</p>"
Eg0
true when the discount is switched on.Egfalse
null.Egnull
null.Egnull
Eg{ ... }
true when the trader is asked for details.Egfalse
true to ask for their name.Egfalse
true to ask for their phone number.Egfalse
true to ask for their address.Egfalse
true to show traders how many are left.Egtrue
true to prioritise the vouchers closest to expiry.Egfalse
Eg{"isEnabled": false, "duration": 7, "timeframe": "days"}
true when the highlight is on.Egfalse
timeframe units.Eg7
duration, such as days.Eg"days"
true when the product's own rules apply; false when it follows its category.Egfalse
Eg[]
true when the rule is active.Egtrue
Ruleuser, role, tag or role_combination
Eg"role"
usernames when you write rules.role or tag.Eg"role"
true when the rule covers all traders.Eg80
true when the special price applies.Egfalse
true when the limit applies.Egfalse
Eg1
intervalUnits.Eg1
months.Eg"months"
Rulefull-access, view-only or access-denied
Eg"full-access"
true to show the exclusive tag.Egfalse
Eg{"isEnabled": false, "status": null}
true when new orders start in status.Egfalse
null when the category or community default applies.Egnull
Eg0
Eg"66f000000000000000000010"
Eg"2026-09-26T08:30:00.000Z"
Eg"2026-09-26T09:15:00.000Z"
{
"meta": {
"status": "success",
"statusCode": 200,
"code": "PRODUCT_UPDATED"
},
"message": "Update product success.",
"data": {
"_id": "66f000000000000000000101",
"communityID": "66f000000000000000000010",
"name": "$25 Trading Credit",
"description": "<p>Redeem 500 coins for a $25 trading credit on your live account.</p>",
"image": "https://cdn.example.com/store/trading-credit-25.png" ,
"price": 500,
"categoryID": "66f000000000000000000201",
"category": "Trading rewards",
"stocks": 2,
"discountPrice": 0,
"isDiscountEnabled": false,
"discountStartDate": null,
"discountEndDate": null,
"userInformation": {
"isEnabled": false,
"shouldCollectName": false,
"shouldCollectPhone": false,
"shouldCollectAddress": false
},
"shouldPrioritiesExpiringVouchers": false,
"shouldDisplayRemainingQuantity": true,
"expiringVoucherHighlight": {
"isEnabled": false,
"duration": 7,
"timeframe": "days"
},
"isArchived": false,
"totalOrders": 0,
"redemptionInstructions": "<p>Your credit is added to your trading account after approval.</p>",
"productPermission": [],
"isProductAccessEnabled": false,
"purchaseStatusOverride": {
"isEnabled": false,
"status": null
},
"createdAt": "2026-09-26T08:30:00.000Z",
"updatedAt": "2026-09-26T09:15:00.000Z"
}
}
Errors#
Every error carries its code in meta.code, with the reason in detail. When the body fails validation, detail is an object keyed by field; when a name has no match or a voucher rule fails, detail is a sentence. A body that isn't valid JSON returns 400 without a meta.code.
Fix the request04
VALIDATION_FAILEDFix the requestdetail lists each problem. Nothing was saved.AUTH_API_KEY_REQUIREDFix the requestAuthorization: Bearer <API_KEY>.AUTH_API_KEY_INVALIDFix the requestAUTH_PERMISSION_REQUIREDFix the requeststore. Add the permission in Settings > Integration > API Keys.Fix the data02
STORE_RESOURCE_CONFLICTFix the datavouchers are already on this product. Nothing was saved; drop those codes and send again.STORE_RESOURCE_NOT_FOUNDFix the dataRetry with backoff03
INTERNAL_ERRORRetry with backoffSTORE_DEPENDENCY_UNAVAILABLERetry with backoffAUTH_API_KEY_VALIDATION_FAILEDRetry with backoff{
"meta": {
"status": "error",
"statusCode": 400,
"code": "VALIDATION_FAILED"
},
"message": "Validation failed",
"detail": {
"description": "description must be a string",
"image": "image must be a URL address",
"categoryName": "categoryName must be a string",
"voucherExpireDate": "voucherExpireDate must be a string"
},
"solution": "Please check the request and try again"
}