Update subscription products
A subscription can handle different type of updates: adding or removing items, and the most common, making an exchange of an item by another one. In any of these case what the API requires is to get the new full cart
Endpoint /api/public/v1/subscriptions/:subscription_id/update_products
Method POST
Swagger https://docs.sequra.com/update/reference/post_api-public-v1-subscriptions-id-update-products#/
Action availability
An update is only accepted while the corresponding subscription action is available. Availability is exposed on the subscription itself — see Checking Availability — and the API enforces it: if the action is not available the request is rejected with 422 and nothing is changed.
| Condition | code returned when not met |
|---|---|
| The subscription is active | invalid — Subscription must be active |
| Its cart is confirmed, so no other change is pending | invalid — Cart must be confirmed |
| The subscription is paid up | subscription_unpaid — Subscription must be paid up to change its products |
The last one is the case of a subscriber in arrears (impago): while there are unpaid charges the products cannot be changed. Check available_actions.change_item before offering the operation rather than relying on the request being accepted.
Rejections use the standard error envelope:
{
"errors": [
{
"code": "subscription_unpaid",
"title": "Subscription must be paid up to change its products"
}
]
}
Shopper confirmation
An update requires the subscriber's confirmation — it is not applied when the request returns.
seQura sends the confirmation SMS automatically. On a successful request the shopper receives an SMS containing a link to review and confirm the changes; you do not need to send anything yourself. The new cart stays unconfirmed until the shopper follows that link, and you have to listen for a webhook to get the confirmation of the operation.
Details worth planning for:
- A
200means the change was registered and the SMS dispatched — not that it took effect. Do not treat the change as applied. - Delivery is best effort: an SMS failure does not fail the request, so a
200is not proof that the shopper received it. - In sandbox the automatic SMS is only delivered when the merchant has sandbox SMS enabled. Do not infer production behaviour from a sandbox test.
- To notify the shopper again, or to notify by email, use
POST /api/public/v1/subscriptions/:id/send_smsorPOST /api/public/v1/subscriptions/:id/send_email.
While a change is awaiting confirmation the cart is not confirmed, so a further update is rejected with Cart must be confirmed until the shopper confirms or the change is cancelled.
Payload
The service will calculate the difference between items based on their item IDs. In this example, the first item doesn't change, so no more data is required. The second item is new. If the previous cart had another item, that item would be removed because its ID is no longer present.
{
"cart_items": [
{
"id": "5025d4dc-7eb2-4686-9b11-cde4e1469f4d"
},
{
"type": "material_subscription",
"reference": "NEW-001",
"name": "New Product",
"price_with_tax": 30000,
"quantity": 1,
"subscription_price": 1200,
"subscription_period": "24 months"
}
]
}
Response
{
"success": true,
"message": "Productos de suscripción actualizados exitosamente"
}
Exchanges and Extensions
What is an Exchange?
An exchange is when a subscriber replaces one or more items in their active subscription with different items — for example, swapping glasses frames or lenses for a new model.
An exchange is only possible when:
- The subscription is active and paid
- The subscriber has remaining exchange quota (see below)
Both conditions are enforced by the API — see Action availability.
Exchange Quota
A subscriber accrues 1 exchange for every completed subscription year, counted from the activation anniversary. Those accrued allowances are cumulative and carry over: a subscriber who has not exchanged during two completed years may make two exchanges.
First-year exchanges are disabled by default. A per-merchant setting enables them, granting one extra allowance during year 1 — before any year has completed.
That first-year allowance is use it or lose it. Unlike the accrued yearly allowances it does not carry over: if year 1 passes without an exchange, it is forfeited and the subscriber is back to one allowance per completed year.
Maximum exchanges allowed in total, by the year the subscription is in:
| Configuration | Year 1 | Year 2 | Year 3 |
|---|---|---|---|
| First year exchanges disabled (default) | 0 | 1 | 2 |
| First year exchanges enabled, year-1 allowance used | 1 | 2 | 3 |
| First year exchanges enabled, year-1 allowance unused | 1 | 1 | 2 |
Only confirmed exchanges count against the quota, and contact-lens exchanges are excluded.
The API exposes both a counter and a boolean:
available_exchanges— number of exchanges remaining in the current quota periodavailable_actions.change_item—truewhen an exchange can be performed right now (quota remaining + subscription active and paid + cart confirmed)
What is an Extension?
An extension is not a customer-facing opportunity — it is a condition that applies automatically when a subscription enters its last year.
Once the subscription crosses the extension_available_on date (12 months before the renewal date), any exchange performed by the subscriber must include an extension: the subscription duration is extended by 12 months as part of the exchange. This ensures the subscriber is never exchanging into a subscription that is about to expire.
When an exchange triggers an extension:
- The subscription duration is extended by 12 months
- The
extension_available_ondate is recalculated for the new period - The exchange quota resets for the new year
Checking Availability
GET /api/public/v1/subscriptions/:id
/api/public/v1/subscriptions/:idThe subscription response includes:
available_actions.change_item
available_actions.change_itemtrue when an exchange is currently available — quota not exceeded, subscription active and paid, cart confirmed.
available_actions.extend_period
available_actions.extend_periodtrue when the subscription has entered its last year (current date is past extension_available_on). When this is true, any exchange must be performed with extend_duration: true.
extension_available_on
extension_available_onThe date from which the extension condition applies.
{
"extension_available_on": "2025-12-01",
"available_actions": {
"change_item": true,
"extend_period": true,
...
}
}Interpreting the Combinations
change_item | extend_period | Meaning |
|---|---|---|
true | false | Exchange available, standard exchange (no extension needed) |
true | true | Exchange available, subscription in last year — exchange must include extension |
false | true | Quota used up for this year, subscription in last year — no exchange possible until next year |
false | false | No exchange available |
Triggering an Exchange
Use the Update Products endpoint:
POST /api/public/v1/subscriptions/:id/update_products
/api/public/v1/subscriptions/:id/update_productsPass the desired final state of items. The API automatically determines what was added, removed, or exchanged.
Relevant parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
apply_yearly_limit | boolean | false | If true, the request fails with 422 when the yearly exchange quota is exceeded |
extend_duration | boolean | false | If true, the subscription is extended by 12 months as part of the exchange |
apply_yearly_limit governs the quota check only. The active, cart-confirmed and paid-up conditions are always enforced and cannot be opted out of.
The value of those parameters is responsibility of merchant. It allows to not extend subscription, or to not reduce exchanges quota when fixing some previous
Standard Exchange (no extension)
When change_item is true and extend_period is false:
{
"cart_items": [
{ "id": "existing-item-uuid" },
{
"type": "material_subscription",
"reference": "NEW-FRAME-001",
"name": "New Frame",
"price_with_tax": 25000
}
],
"apply_yearly_limit": true
}Exchange with Extension
When extend_period is true, pass extend_duration: true. The exchange will extend the subscription by 12 months:
{
"cart_items": [
{ "id": "existing-item-uuid" },
{
"type": "material_subscription",
"reference": "NEW-FRAME-001",
"name": "New Frame",
"price_with_tax": 25000
}
],
"apply_yearly_limit": true,
"extend_duration": true
}Updated about 1 month ago