Returning.AIDevelopers
v1

API reference / Store / Products

.md

Update product and append vouchers

Replace one Store product's settings with a full body, and add any new voucher codes to its stock.

Last updated 26 Sep 2026API v1

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#

productID#stringREQUIRED
Product ID, from List products or Create product with vouchers.

Rule24-character hex ID

Eg66f000000000000000000101

Headers#

Authorization#stringREQUIRED
Community API key with store.

RuleBearer <API_KEY>

Content-Type#stringREQUIRED
Request body format.

Ruleapplication/json

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: "".

name#stringREQUIRED
Product name shown in the Store.

Rule1-100 characters

Eg"$25 Trading Credit"

description#stringREQUIRED
Product description. HTML is allowed; only 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>"

image#stringREQUIRED
Image URL, or null for no image.

Rulehttps:// URL or null

Eg"https://cdn.example.com/store/trading-credit-25.png"

price#numberREQUIRED
Price in coins.

RuleNumber, 0 or more

Eg500

categoryName#stringREQUIRED
Exact name of an existing category in your community. null removes the category.

RuleExisting category name or null

Eg"Trading rewards"

vouchers#string[]REQUIRED
Voucher codes to add as stock, one unit each. Existing codes stay; this never removes or replaces codes. A code the product already has returns 409. Send [] to add none.

RuleArray of non-empty strings

Eg["TC25-8R5N-3C1Y"]

voucherExpireDate#stringREQUIRED
Expiry date for every code in 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"

isArchived#booleanREQUIRED
true archives the product: it leaves List products and the Store. false shows it again.

Egfalse

redemptionInstructions#stringREQUIRED
Redemption instructions for the trader, as text or HTML, or null.

RuleString or null

Eg"<p>Your credit is added to your trading account after approval.</p>"

discountPrice#numberREQUIRED
Discounted price in coins, charged between the discount dates while isDiscountEnabled is true. Send 0 when there is no discount.

Rule0 or more; below price when the discount is on

Eg0

isDiscountEnabled#booleanREQUIRED
Switches the discount on or off.

Ruletrue needs both discount dates

Egfalse

discountStartDate#stringREQUIRED
When the discount starts, or null.

RuleISO 8601 date or null; before discountEndDate

Egnull

discountEndDate#stringREQUIRED
When the discount ends, or null.

RuleISO 8601 date or null; after discountStartDate

Egnull

userInformation#objectREQUIRED
What to ask the trader for when they redeem. Send all four flags.

Eg{ ... }

isEnabled#booleanREQUIRED
true to ask for details.

Egfalse

shouldCollectName#booleanREQUIRED
Ask for the trader's name.

Egfalse

shouldCollectPhone#booleanREQUIRED
Ask for the trader's phone number.

Egfalse

shouldCollectAddress#booleanREQUIRED
Ask for the trader's address.

Egfalse

shouldDisplayRemainingQuantity#booleanREQUIRED
true to show traders how many are left.

Egtrue

shouldPrioritiesExpiringVouchers#booleanREQUIRED
true to prioritise the vouchers closest to expiry.

Egfalse

expiringVoucherHighlight#objectOPTIONAL
Highlight for vouchers close to expiry. Omit it to keep the current setting; when you send it, send all three fields.

Eg{"isEnabled": false, "duration": 7, "timeframe": "days"}

isEnabled#booleanREQUIRED
true to turn the highlight on.

Egfalse

duration#integerREQUIRED
How close to expiry, in timeframe units.

RuleWhole number, 0 or more

Eg7

timeframe#stringREQUIRED
Unit for duration, such as days.

RuleString

Eg"days"

isProductAccessEnabled#booleanREQUIRED
true to apply the rules in productPermission; false to follow the category. Saved as false when productPermission is empty.

Egfalse

productPermission#object[]REQUIRED
Access rules, one entry per rule. Replaces every existing rule; [] removes them all. Names are matched in your community.

RuleArray

Eg[]

isEnabled#booleanREQUIRED
true when the rule is active.

Egtrue

type#stringREQUIRED
What the rule matches.

Ruleuser, role, tag or role_combination

Eg"role"

permission#objectOPTIONAL
The traders the rule covers, by name. Optional; when you send it, send all five fields.
roleNames#string[]REQUIRED
Exact role names. [] for none.

RuleExisting role names

tagNames#string[]REQUIRED
Exact tag names. [] for none.

RuleExisting tag names

usernames#string[]REQUIRED
Usernames of traders, matched exactly and case-sensitively. A trader created with Create User may not match until they have signed in once; until then the request returns 400 with Unable to resolve usernames: <names>. [] for none.

RuleExisting usernames; case-sensitive

combination#object[]REQUIRED
Role or tag groups a trader must hold together. [] for none.
type#stringREQUIRED
Whether values are role or tag names.

Rulerole or tag

Eg"role"

values#string[]REQUIRED
Role or tag names in the group.
isSelectAll#booleanREQUIRED
true when the rule covers all traders. Required whenever you send permission.

Egfalse

purchaseAccess#objectOPTIONAL
Special price for traders the rule covers. Omit it for no special price.
specialPrice#numberREQUIRED
Special price in coins.

Rule0 or more

Eg80

isSpecialPriceEnabled#booleanREQUIRED
true to charge the special price.

Egfalse

purchaseLimit#objectOPTIONAL
Purchase limit for traders the rule covers. Omit it for no limit.
isPurchaseLimitEnabled#booleanREQUIRED
true to apply the limit.

Egfalse

quantity#numberREQUIRED
Purchases allowed in each window.

RuleWhole number, 0 or more

Eg1

intervalCount#numberREQUIRED
Length of the window, in intervalUnits.

RuleWhole number, 1 or more

Eg1

intervalUnit#stringREQUIRED
Unit of the window.

Ruledays, weeks, months or years

Eg"months"

accessLevel#stringREQUIRED
What covered traders can do. null means full access.

Rulefull-access, view-only, access-denied or null

Eg"full-access"

isExclusiveTagEnabled#booleanREQUIRED
true to show the exclusive tag.

Egfalse

purchaseStatusOverrideName#stringREQUIRED
Name of the order status new orders for this product start in, or 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.

meta#objectALWAYS
Status details.

Eg{ ... }

status#stringALWAYS
Result of the request.

Rulesuccess

statusCode#integerALWAYS
The HTTP status, repeated.

Eg200

code#stringALWAYS
Machine-readable result code.

RulePRODUCT_UPDATED

message#stringALWAYS
Human-readable summary. Do not branch on it.

Eg"Update product success."

data#objectALWAYS
The product after the update.

Eg{ ... }

_id#stringALWAYS
Product ID. Use it to read or update the product.

Eg"66f000000000000000000101"

name#stringALWAYS
Product name shown in the Store.

Eg"$25 Trading Credit"

price#numberALWAYS
Price in coins.

Eg500

categoryID#stringALWAYS
Category ID, or null when the product has no category.
category#stringALWAYS
Category name, or null when the product has no category or its category was deleted.

Eg"Trading rewards"

stocks#integerALWAYS
Vouchers left to buy: codes that are unsold and not expired. Voucher codes are never returned.

Eg2

isArchived#boolean
true when the product is archived and left out of List products and the Store.

Egfalse

image#string
Image URL, or null when there is no image.

Eg"https://cdn.example.com/store/trading-credit-25.png"

description#string
Product description as HTML. Only basic formatting tags are kept.

Eg"<p>Redeem 500 coins for a $25 trading credit on your live account.</p>"

redemptionInstructions#string
Redemption instructions for the trader, or null.

Eg"<p>Your credit is added to your trading account after approval.</p>"

discountPrice#number
Discounted price in coins, charged between the discount dates while the discount is on.

Eg0

isDiscountEnabled#boolean
true when the discount is switched on.

Egfalse

discountStartDate#string
When the discount starts, ISO 8601 UTC, or null.

Egnull

discountEndDate#string
When the discount ends, ISO 8601 UTC, or null.

Egnull

userInformation#object
What the trader is asked for when they redeem.

Eg{ ... }

isEnabled#boolean
true when the trader is asked for details.

Egfalse

shouldCollectName#boolean
true to ask for their name.

Egfalse

shouldCollectPhone#boolean
true to ask for their phone number.

Egfalse

shouldCollectAddress#boolean
true to ask for their address.

Egfalse

shouldDisplayRemainingQuantity#boolean
true to show traders how many are left.

Egtrue

shouldPrioritiesExpiringVouchers#boolean
true to prioritise the vouchers closest to expiry.

Egfalse

expiringVoucherHighlight#object
Highlight for vouchers close to expiry.

Eg{"isEnabled": false, "duration": 7, "timeframe": "days"}

isEnabled#boolean
true when the highlight is on.

Egfalse

duration#integer
How close to expiry, in timeframe units.

Eg7

timeframe#string
Unit for duration, such as days.

Eg"days"

isProductAccessEnabled#boolean
true when the product's own rules apply; false when it follows its category.

Egfalse

productPermission#object[]ALWAYS
Access rules. An empty array means the product has no rules of its own.

Eg[]

isEnabled#boolean
true when the rule is active.

Egtrue

type#string
What the rule matches.

Ruleuser, role, tag or role_combination

Eg"role"

permission#object
The traders the rule covers, by readable name.
roleNames#string[]
Role names.
tagNames#string[]
Tag names.
usernames#string[]
Usernames.
userIDs#string[]
Record IDs of the traders the rule names. Send usernames when you write rules.
combination#object[]
Role or tag groups a trader must hold together.
type#string
role or tag.

Eg"role"

values#string[]
Role or tag names in the group.
isSelectAll#boolean
true when the rule covers all traders.
purchaseAccess#object
Special price for traders the rule covers.
specialPrice#number
Special price in coins.

Eg80

isSpecialPriceEnabled#boolean
true when the special price applies.

Egfalse

purchaseLimit#object
How often a covered trader can buy.
isPurchaseLimitEnabled#boolean
true when the limit applies.

Egfalse

quantity#number
Purchases allowed in each window.

Eg1

intervalCount#number
Length of the window, in intervalUnits.

Eg1

intervalUnit#string
Unit of the window, such as months.

Eg"months"

accessLevel#string
What covered traders can do.

Rulefull-access, view-only or access-denied

Eg"full-access"

isExclusiveTagEnabled#boolean
true to show the exclusive tag.

Egfalse

purchaseStatusOverride#objectALWAYS
The status new orders for this product start in, when set.

Eg{"isEnabled": false, "status": null}

isEnabled#boolean
true when new orders start in status.

Egfalse

status#string
Order status name, or null when the category or community default applies.

Egnull

totalOrders#integer
Orders placed for this product.

Eg0

communityID#string
Your community ID.

Eg"66f000000000000000000010"

createdAt#string
When the product was created, ISO 8601 UTC.

Eg"2026-09-26T08:30:00.000Z"

updatedAt#string
When the product last changed, ISO 8601 UTC.

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

400VALIDATION_FAILEDFix the request
A field is missing or breaks a rule, or a name has no match. This endpoint needs the full body; detail lists each problem. Nothing was saved.
401AUTH_API_KEY_REQUIREDFix the request
No key was sent. Send Authorization: Bearer <API_KEY>.
401AUTH_API_KEY_INVALIDFix the request
The key is unknown, expired or malformed. Use a current Community API key.
403AUTH_PERMISSION_REQUIREDFix the request
The key lacks store. Add the permission in Settings > Integration > API Keys.

Fix the data02

409STORE_RESOURCE_CONFLICTFix the data
One or more codes in vouchers are already on this product. Nothing was saved; drop those codes and send again.
404STORE_RESOURCE_NOT_FOUNDFix the data
No product with this ID in your community. Find the right ID with List products.

Retry with backoff03

500INTERNAL_ERRORRetry with backoff
The update may be partly saved, with the new fields but without the new codes. Read the product back, then send the same request again.
502STORE_DEPENDENCY_UNAVAILABLERetry with backoff
The Store was unreachable. Read the product back, then send the same request again with backoff.
401AUTH_API_KEY_VALIDATION_FAILEDRetry with backoff
The key could not be checked just now. Retry with backoff; the key itself may be fine.
{
  "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"
}

Next step#

Read productGET/v1/products/{productID}Read the product back to confirm the new settings and stocks.