Skip to main content

Changes and undo

Most configuration writes made through the API are recorded as changes: store settings, design and theme, home page sections, categories, stock, promo codes, pixels, shipping rates and settings, and landing page sections. Each change keeps the values it replaced, and an undo writes them back through the same checks as the original write. Writes made on the store by Copilot, a connected AI assistant (Claude or ChatGPT) or an installed app are recorded too. Changes saved in the dashboard are not.

The three endpoints below list the changes, read one in full and undo one. The writes under /v1/store/home-layout and the courier rate sync answer with a change_id. For the other writes, find the change with GET /v1/changes.

What is recorded​

entityRecorded byentity_idScope to read it in fullScope to undo it
store.settingsPATCH /v1/storesettingsstore:readstore:write
store.designPATCH /v1/store/designdesignstore:readstore:write
store.themePOST /v1/store/theme, POST /v1/store/fast-checkout-theme, POST /v1/store/variant-stylethemestore:readstore:write
store.home_sectionsPATCH /v1/store/home-sectionshome_sectionsstore:readstore:write
store.home_layoutEvery write under /v1/store/home-layoutlayoutstore:readstore:write
categoryPOST /v1/categories, PATCH and DELETE /v1/categories/{id}Category idproducts:readproducts:write
stockPOST /v1/products/{id}/stockProduct idproducts:readproducts:write
promo_codePOST /v1/promo-codes, PATCH and DELETE /v1/promo-codes/{id}Promo code idpromos:readpromos:write
pixelsPOST /v1/pixels, PATCH and DELETE /v1/pixels/{id}The pixel's id on DZBuild, not its pixel_idpixels:readpixels:write
shipping.ratesPOST /v1/shipping/rates, POST /v1/shipping/rates/syncratesshipping:readshipping:write
shipping.settingsPATCH /v1/shipping/settingssettingsshipping:readshipping:write
lp.sectionThe section writes under /v1/landing-pages/{id}/sectionsSection id, or lp: and the page id for a reorderlanding_pages:readlanding_pages:write
  • These writes are not recorded and cannot be undone: products with their images, variants, offers, add-ons and quantity rules (stock is recorded), orders, landing pages themselves, the category order, couriers, webhooks and keys.
  • lp.page is accepted as an entity filter, but no write records it.
  • Recording does not block a write. When a change cannot be recorded, for example because its before or after values pass 256 KB, the write still goes through and cannot be undone. Shipping rate writes are the exception: they check the size first and answer 422 snapshot_too_large instead of running.
  • Every scope in the table is among the default scopes of a new key, so a key created from the dashboard (Settings → API, /dashboard/api) can use the three endpoints. Scopes are frozen when a key is created, so an older key that lacks the scope it needs answers 403 forbidden: create a new key from the dashboard.

GET /v1/changes​

The store's changes, newest first, without the values they replaced.

Auth: platform key with store:read. An installed app's token gets 403 forbidden (Apps cannot use this endpoint).

Query parameters​

ParamTypeDefaultNotes
entitystringnoneOnly the changes of one entity from the table above. Any other value is ignored and the whole list comes back.
limitint251 to 100. A smaller value counts as 1, a larger one as 100.
cursorstringnonenext_cursor of the previous page. See Pagination.

Request​

curl 'https://api.dzbuild.app/v1/changes?limit=2' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

{
"data": {
"items": [
{
"id": 120,
"entity": "shipping.rates",
"entity_id": "rates",
"action": "update",
"summary": "Shipping rates updated for 2 wilaya(s)",
"undone_at": null,
"created_at": "2026-10-06 11:02:17",
"undone": false
},
{
"id": 119,
"entity": "promo_code",
"entity_id": "7",
"action": "update",
"summary": "Updated promo code SUMMER10",
"undone_at": null,
"created_at": "2026-10-06 10:52:30",
"undone": false
}
],
"next_cursor": "MTE5",
"has_more": true
}
}
FieldMeaning
idThe change id, for GET /v1/changes/{id} and the undo.
entityWhat was changed. See the table above.
entity_idWhich item was changed, as a string. See the table above.
actioncreate, update or delete. An undo is recorded as an update.
summaryA short description in English. An undo reads Undo of change # followed by the id it undid.
undonetrue once the change has been undone.
undone_atWhen the change was undone, otherwise null.
created_atYYYY-MM-DD HH:MM:SS, server time. undone_at uses the same format.

GET /v1/changes/{id}​

One change with before, the values it replaced, and after, the values it wrote. Their shape depends on the entity: some keep only the fields the write touched, others the whole item.

Auth: platform key with store:read, plus the read scope of the change's entity from the table above. An installed app's token gets 403 forbidden.

Request​

curl 'https://api.dzbuild.app/v1/changes/118' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

{
"data": {
"id": 118,
"key_id": "dzpk_live_xxxxxxxxxxxxxx",
"entity": "category",
"entity_id": "12",
"action": "update",
"summary": "Updated category #12 (name, slug)",
"undone_at": null,
"undone_by_id": null,
"created_at": "2026-10-06 10:41:05",
"before": {"name": "Shoes", "slug": "shoes"},
"after": {"name": "Sneakers", "slug": "sneakers"},
"undone": false
}
}

The answer carries the fields of the list, plus these.

FieldMeaning
key_idThe key that made the change, or null for an undo made from the dashboard.
undone_by_idThe id of the change that undid this one, otherwise null.
beforeThe values the change replaced. null for a create.
afterThe values the change wrote. null for a delete and for a courier rate sync.

A pixel's access token is never kept in a change. When the pixel has one, before and after show •••••••• in its place.

Errors​

HTTPCodeCause
400bad_requestThe id in the path is not all digits.
403forbiddenThe key lacks store:read or the read scope of the change's entity (Missing scope: ...), or the call comes from an installed app's token.
404not_foundNo change with this id in the store.

POST /v1/changes/{id}/undo​

Writes the change's before values back through the same checks as the original write, then records the undo as a new change. No request body.

Auth: platform key with the undo scope of the change's entity from the table above; store:read is not needed. Requires Idempotency-Key. An installed app's token gets 403 forbidden (Apps cannot use this endpoint). The store owner sees the last 20 changes of each installed app on that app's page in the dashboard (/dashboard/apps/{id}, in the list of its latest changes) and can undo them there with the undo button.

What the undo does​

  1. A change that created something has nothing to restore and answers 422 nothing_to_restore: delete the item instead. Adding a home page section through /v1/store/home-layout is the exception, because every home layout write is recorded as an update of the whole layout.
  2. An update is undone by writing the before values back over what is there now. Only the home page checks for later changes and answers 409 layout_changed (see Home page sections). To walk back several changes to one item, undo them newest first.
  3. A deleted category comes back with its id, without its image. A deleted promo code comes back with its id when no other code has taken it, and with its usage count.
  4. A deleted pixel comes back with its id when it is still free, but without its access token and without its product, category and landing page assignments. Undoing a pixel update leaves its current access token in place.
  5. A deleted landing page section comes back with a new id.
  6. A stock undo sets each value back to the number recorded before the change, whatever orders did to the stock since.
  7. Undoing POST /v1/shipping/rates puts the prior prices back, and removes the rate of a wilaya that had none before the change. Undoing a courier rate sync puts back every price the sync overwrote, and keeps the rates it added for wilayas that had none.
  8. The undo is recorded as a change of its own, undo_change_id, which you can undo to apply the original change again. When the original change has no after (a delete or a courier rate sync), undoing the undo answers 422 nothing_to_restore.
  9. A change can be undone once. A later undo answers 409 already_undone, and of two undos sent at the same moment only one runs.
  10. When the restore itself fails (restore_target_missing, a refused value or a 500), the change is not marked as undone, so you can retry once the cause is fixed. The retry rules are below.

The undo answer does not carry the restored values. Read the item again. Through api.dzbuild.app, the short read cache (GET /v1/store and every GET under it, the GET /v1/products and GET /v1/landing-pages lists) can still return the old values for up to 30 seconds after the undo.

Request​

curl -X POST 'https://api.dzbuild.app/v1/changes/118/undo' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: undo-118"

Response 200​

{
"data": {
"undone": true,
"change_id": 118,
"entity": "category",
"undo_change_id": 121
}
}
FieldMeaning
undoneAlways true on a 200.
change_idThe change that was undone.
entityIts entity.
undo_change_idThe change that records this undo, or null when it could not be recorded.

Errors​

HTTPCodeCause
400bad_requestThe id in the path is not all digits, or Idempotency-Key is missing or malformed.
403forbiddenThe key lacks the undo scope of the change's entity (Missing scope: ...), or the call comes from an installed app's token.
404not_foundNo change with this id in the store.
409already_undoneThe change was already undone.
409layout_changedHome page only: the layout changed after this change.
422not_undoableThis kind of change cannot be undone.
422nothing_to_restoreThe change created something, or holds no values to restore. Delete the item instead.
422restore_target_missingWhat the change touched no longer exists, for example a category or a landing page section deleted since.
422idempotency_key_reuseThe same Idempotency-Key was already used for a different request, such as the undo of another change.
4xxThe original write's codeThe checks of the original write refuse the values, for example invalid_wilaya on shipping rates, or a theme that is no longer offered.
500server_errorThe undo could not run. The change stays undoable.

Retries and Idempotency-Key​

The first answer to a key is stored for 24 hours, a 4xx included. A retry with the same key for the same change gets that answer back with Idempotency-Replay: 1, and no second undo runs. So after you fix the cause of an error, retry with a new key. A 5xx or 429 answer is never stored, so retry it with the same key. See Idempotency.

This page for AI toolsView as MarkdownOpen in ChatGPTOpen in Claude