Undo and confirmations
Every change your app or agent makes is logged with before/after values and can be undone for 30 days; irreversible actions ask for a two-step confirmation first.
Two rules keep a store safe when an app (or an AI agent) operates it:
- Everything can be traced and most things can be undone. Each change is recorded in the action log with who made it and the exact values before and after. For 30 days you can undo it.
- What can't be undone asks first. Delisting, disconnecting a channel, removing variants, refunding a storefront order, undoing an import and closing a company all use the same two-step confirmation.
Find what was done
Every write answers with a Nembol-Action-Id header, irreversible ones
included (shipping an order, delisting, disconnecting). A replay of the same
Idempotency-Key (Idempotent-Replayed: true) isn't a new write: it records
no action and has no header. The full log:
curl "https://api.nembol.com/rest/v1/actions?product_id=prod_66f0a1" \
-H "Authorization: Bearer $NEMBOL_API_KEY"{
"object": "list",
"data": [
{
"id": "act_66f0b2",
"object": "action",
"type": "product.update",
"summary": "Edited product \"Blue mug\"",
"actor": { "type": "api_key", "name": "Inventory agent", "via": "mcp" },
"reversibility": "reversible",
"undoable": true,
"undoable_until": "2026-11-07T10:00:00.000Z",
"items": [
{
"product_id": "prod_66f0a1",
"changes": [
{ "path": "fields.title", "before": "Blue mug", "after": "Big blue mug" },
{ "path": "variants.66f0a2.price", "before": "10.00", "after": "12.00" }
]
}
],
"created_at": "2026-10-08T10:00:00.000Z"
}
]
}Logged: product create/edit/delete/restore, variant and inventory changes,
price adjustments, product batches, imports, channel-account settings (also
when changed in the dashboard), publishes, delists, shipments and
connect/disconnect. Kept 90 days. Reading needs events:read or
products:read.
Undo
Preview first:
curl -X POST https://api.nembol.com/rest/v1/actions/act_66f0b2/undo \
-H "Authorization: Bearer $NEMBOL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dry_run": true}'{
"object": "undo_preview",
"status": "simulated",
"outcome": "partial",
"items": [
{
"product_id": "prod_66f0a1",
"restored": ["variants.var_66f0a2.price"],
"unchanged": [],
"conflicts": [{ "path": "fields.title", "reason": "changed_since", "current": "Mug v3" }]
}
]
}A dry run never undoes anything: status is always simulated and
outcome says what the real undo would answer (undone, partial,
conflict, nothing_to_undo). A merge undo lists every product: the
survivor (role: "survivor": fields, variants and listings that go back) and
each absorbed one (role: "absorbed": it comes back with its listings).
Then the same call without dry_run. How each value is treated:
| The value now | What the undo does |
|---|---|
| Still what the action wrote | Puts the old value back |
| Already the old value | Nothing (unchanged) |
| Changed again since, by someone or by a channel | Leaves it and reports a conflict |
| A quantity | Reverses the change as a delta: if the action set 5 → 8 and 2 sold since (6), the undo makes it 3. Sold stock is never re-added. Below zero → conflict |
Restored products are re-sent to their channels. The undo is itself an action
(undo_of points to the original) and an action can be undone once.
| Action | Undo |
|---|---|
| Product edit, variant/inventory change, price adjustment, batch | Values restored (re-synced to channels) |
| Product delete | Product and its images back (also POST /products/{id}/restore) |
| Product create | Deleted again, unless it's live on a channel |
| Channel-account settings | Settings restored |
| Import | Created products removed and links undone (nothing on the channel) |
| Publish | Not automatic. Delist it (DELETE /listings/{id}); channel fees aren't refunded |
| Delist, shipment notice, disconnect | Irreversible (see below) |
Deleted products and their images
A deleted product keeps its images for 30 days, so restoring it within that time brings them back. After 30 days the images are removed; the product data can still be restored, without them.
Two-step confirmation
Irreversible actions (DELETE /listings/{id}, POST /listings/batch with
action: "delist", disconnecting a channel account…) first answer 409.
Shipping an order (POST /orders/{id}/fulfillment) doesn't ask: it's the
automated step of a fulfillment system, and the marketplace is notified at
most once per order.
{
"error": {
"type": "invalid_request_error",
"code": "confirmation_required",
"message": "Delisting ends this listing on its channel. Publishing it again later creates a NEW listing … repeat the SAME request with header \"Nembol-Confirmation: cfm_3kTq9Xb2\" within 10 minutes."
},
"confirmation": {
"token": "cfm_3kTq9Xb2",
"summary": "Delisting ends this listing on its channel. …",
"expires_at": "2026-10-08T10:10:00.000Z"
}
}Show confirmation.summary to the person. If they agree, send the same
request again with the header:
curl -X DELETE https://api.nembol.com/rest/v1/listings/list_cHJvZF8x \
-H "Authorization: Bearer $NEMBOL_API_KEY" \
-H "Nembol-Confirmation: cfm_3kTq9Xb2"Nembol asks for the confirmation only once the request is valid and the action is possible, so your app never asks a person to confirm something that can't happen:
- an unknown company, import, listing or order answers
404straight away; - an import already undone or a listing that can't be delisted answers its own error straight away;
- closing a company that is already closed answers
200with the company as it is: closing is idempotent, nothing to confirm; - a batch delist counts in its summary only the listings that would really end; when none would (unknown, already delisted, not delistable), it answers the per-item outcomes without a confirmation.
The summary names what the action is about (the product and the shop for a delist, the shop for a disconnect, the company for a close), so you can show it to the person as is.
The token works once, for that exact request, for 10 minutes. It's asked for
keys created from 2026-10-08 and for every call made through the Nembol MCP
server (Nembol-Client: mcp).
Good practice for agents
- Keep the
Nembol-Action-Idof what you did, so you can undo it precisely. Never "undo the last thing" blindly. - Preview with
dry_runand tell the person about conflicts instead of forcing values. - Never confirm on the person's behalf: a
confirmation_requiredanswer is the moment to ask them.
Last updated on October 18, 2026
Need help?
Email us and a human replies, usually within one business day.