Undo a program change
POST /program/changes/{change_id}/undo| Scope | program:write |
| Customer session token | Not accepted: call from your server with an API key |
| Rate limit class | write_shopify (limits) |
| Idempotency key | Required (idempotency) |
| MCP tool | charm_undo_program_change |
Puts the rule, reward, tier or setting back to how it was before the change, through the same save — so the storefront follows, and the undo is logged as a change of its own. Refused with 409 revision_conflict when the target changed again since, was undone already, or the program moved past if_revision. A change that flips a program switch or touches a setting outside PATCH /program/settings cannot be undone here (422). Undoing an undo re-applies the original change. Call with dry_run: true first.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
change_id | path | string | The change’s id from GET /program/changes. |
Request body
Section titled “Request body”| Field | Type | Description |
|---|---|---|
dry_run | boolean | Preview only: validate, resolve references and return the diff and summary, but write nothing. Costs no idempotency key and is limited as a read. Call with this first, show the merchant, then repeat without it. (optional) |
if_revision | integer | The program revision you last read (from GET /program or a previous write). When the program changed since, the write is refused with 409 revision_conflict instead of overwriting a concurrent admin edit. (optional) |
Response 200
Section titled “Response 200”Returns ProgramUndoResult.
| Field | Type | Description |
|---|---|---|
dry_run | boolean | |
revision | integer | |
operation | string: create, update, delete, none | |
diff | array of FieldChange | |
summary | string or null | (nullable) |
undone | ProgramChange |
{ "dry_run": false, "revision": 1, "operation": "create", "diff": [ { "path": "path", "before": null, "after": null } ], "summary": "summary", "undone": { "id": "id", "at": "2026-10-01T09:30:00.000Z", "actor": { "type": "admin", "id": "id", "label": "Points per order" }, "operation": "create", "target": { "kind": "earning_rule", "id": "id", "label": "Points per order" }, "diff": [ { "path": null, "before": null, "after": null } ], "revision": 1, "undone_at": "2026-10-01T09:30:00.000Z", "undo_of": "undo of", "summary": "summary", "undoable": false }}Example
Section titled “Example”curl -X POST "https://charm.appfleece.app/api/v1/program/changes/<change_id>/undo" \ -H "Authorization: Bearer chrm_live_..." \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"dry_run":false,"if_revision":1}'Errors
Section titled “Errors”unauthorized · invalid_token · insufficient_scope · access_required · not_found · validation_error · rate_limited · idempotency_conflict · shopify_unavailable
Every error body carries code, message, request_id and a doc_url pointing at the matching entry in the error catalog.