Copy as markdown[View .md](https://docs.funnelfizz.com/ai-agents/revert-changes "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Frevert-changes.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Reverting%20changes "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Frevert-changes.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Reverting%20changes "Open this page in ChatGPT with context")

# Reverting changes

Every MCP mutation is recorded in `mcp_changes`, a per-workspace change feed. Your agent can list recent changes and revert any that fall within the 24-hour window.

## The two tools[​](#the-two-tools "Direct link to The two tools")

### `mcp.list_changes`[​](#mcplist_changes "Direct link to mcplist_changes")

```
mcp.list_changes({ limit?: number, cursor?: string })

  → {

      changes: Array<{

        id: string,

        kind: string,         // tool name, e.g. "workspace.update_brand"

        primaryEntityKind: string,

        primaryEntityId: string,

        summary: string,

        revertible: boolean,

        revertibleUntil: ISO timestamp,

        revertedAt?: ISO timestamp,

        createdAt: ISO timestamp,

      }>,

      nextCursor?: string,

    }
```

Returns the workspace's change feed, newest first. Includes both revertible changes and **tombstone rows** (`revertible: false`) for irreversible actions like sends.

### `mcp.revert_change`[​](#mcprevert_change "Direct link to mcprevert_change")

```
mcp.revert_change({ changeId: string })

  → { reverted: true, summary: string }

  | { error: "not_revertible" | "expired" | "already_reverted" | ... }
```

Reverts the named change. Revert behavior depends on the kind:

* **Create-style** (`funnel.split.create`, `email.template.create`, `api_key.create`): the created entity is archived/deleted.
* **Update-style** (`workspace.update_brand`, `email.template.update`, `team.change_role`): the prior state is restored from `previousState` snapshot.
* **Tombstone** (`email.draft.send_now`, `billing.cancel_subscription`, `workspace.delete`): cannot be reverted. The entry exists in the feed for audit/visibility only.

## Tombstone rows[​](#tombstone-rows "Direct link to Tombstone rows")

Some actions cannot be undone. Email sends leave the inbox; cancelled subscriptions trigger Stripe webhooks; deleted workspaces cascade-delete millions of rows. For these, `mcp_changes` still records the change (with `revertible: false`) so the agent's change feed stays complete — but `mcp.revert_change` will error with `not_revertible`.

| Action                                                                                | Why tombstone                           |
| ------------------------------------------------------------------------------------- | --------------------------------------- |
| `email.draft.send_now`                                                                | Emails left the building                |
| `email.campaign.send_test`                                                            | Test email sent                         |
| `billing.cancel_subscription` / `billing.resume_subscription` / `billing.change_plan` | Real money flowed                       |
| `team.remove_member` (post-accept)                                                    | The user already had access             |
| `api_key.revoke`                                                                      | Secret destroyed                        |
| `tracking.site.delete` / `email.domain.delete` / `email.sender.delete`                | Cascade ripples through linked entities |
| `workspace.delete` / `workspace.transfer_ownership`                                   | Fundamentally one-way                   |

## The 24-hour window[​](#the-24-hour-window "Direct link to The 24-hour window")

`revertibleUntil` is 24 hours after `createdAt`. After that, the change is read-only history. Tombstones never become revertible regardless of time.

## Snapshot-and-restore for updates[​](#snapshot-and-restore-for-updates "Direct link to Snapshot-and-restore for updates")

Update-style changes write a JSON snapshot of the entity's prior state into `mcp_changes.previousState`. On revert, the snapshot is written back via `tx.update(...).set(snapshot)`. This works for any single-entity update — workspace settings, email template body, sender display name, etc.

For multi-entity creates (a single `funnel.split.create` call creates a parent split row, multiple FunnelTrack rows, multiple FunnelStage rows, and may modify the funnel's canvasState), the revert handler knows to undo all of them via the `autoCreatedEntities` array.

## UI awareness[​](#ui-awareness "Direct link to UI awareness")

The dashboard polls `mcp.list_changes` every 30 seconds. When new agent-driven changes appear:

* A toast notification shows "Agent made N change(s). Refresh to see latest."
* Clicking the toast refreshes the active page's data.
* Users can also browse the full feed in **Settings → Developer → Activity** alongside the per-call audit log.

This means if you and an agent edit the same template at the same time, you'll see the agent's change within 30 seconds and can choose to revert it before your edits land.

## Agent etiquette[​](#agent-etiquette "Direct link to Agent etiquette")

Good agents:

* Surface changes that touched the user's recent context (e.g. "I just updated workspace brand from #7c3aed to #ff5733 — say 'revert that' if you'd rather keep the old color")
* Offer revert proactively after destructive-feeling mutations
* Don't list every change in a chatty way; the feed is for audit, not narration

## Related[​](#related "Direct link to Related")

* [Safety tiers](https://docs.funnelfizz.com/ai-agents/safety-tiers.md) — what's revertible and what isn't.
* [MCP](https://docs.funnelfizz.com/ai-agents/mcp.md) — main MCP overview.
