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
| Recorded by |
| Scope to read it in full | Scope to undo it |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Every write under |
|
|
|
|
| Category id |
|
|
|
| Product id |
|
|
|
| Promo code id |
|
|
|
| The pixel's id on DZBuild, not its |
|
|
|
|
|
|
|
|
|
|
|
|
| The section writes under | Section id, or |
|
|
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.pageis accepted as anentityfilter, 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_largeinstead 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 answers403 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
Param | Type | Default | Notes |
| string | none | Only the changes of one |
| int | 25 | 1 to 100. A smaller value counts as 1, a larger one as 100. |
| string | none |
|
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
}
}
Field | Meaning |
| The change id, for |
| What was changed. See the table above. |
| Which item was changed, as a string. See the table above. |
|
|
| A short description in English. An undo reads |
|
|
| When the change was undone, otherwise |
|
|
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.
Field | Meaning |
| The key that made the change, or |
| The id of the change that undid this one, otherwise |
| The values the change replaced. |
| The values the change wrote. |
A pixel's access token is never kept in a change. When the pixel has one, before and after show •••••••• in its place.
Errors
HTTP | Code | Cause |
400 |
| The id in the path is not all digits. |
403 |
| The key lacks |
404 |
| No 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
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-layoutis the exception, because every home layout write is recorded as anupdateof the whole layout.An
updateis undone by writing thebeforevalues back over what is there now. Only the home page checks for later changes and answers409 layout_changed(see Home page sections). To walk back several changes to one item, undo them newest first.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.
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.
A deleted landing page section comes back with a new id.
A stock undo sets each value back to the number recorded before the change, whatever orders did to the stock since.
Undoing
POST /v1/shipping/ratesputs 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.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 noafter(a delete or a courier rate sync), undoing the undo answers422 nothing_to_restore.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.When the restore itself fails (
restore_target_missing, a refused value or a500), 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: every GET through api.dzbuild.app is served fresh, so the read right after the undo returns the restored values.
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
}
}
Field | Meaning |
| Always |
| The change that was undone. |
| Its entity. |
| The change that records this undo, or |
Errors
HTTP | Code | Cause |
400 |
| The id in the path is not all digits, or |
403 |
| The key lacks the undo scope of the change's entity ( |
404 |
| No change with this id in the store. |
409 |
| The change was already undone. |
409 |
| Home page only: the layout changed after this change. |
422 |
| This kind of change cannot be undone. |
422 |
| The change created something, or holds no values to restore. Delete the item instead. |
422 |
| What the change touched no longer exists, for example a category or a landing page section deleted since. |
422 |
| The same |
4xx | The original write's code | The checks of the original write refuse the values, for example |
500 |
| The 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.