Undo Database Changes Made by an AI Agent, Step by Step

To undo database changes an AI agent made, you need a record of exactly what that agent changed, with the old values, kept separately from everyone else's writes. Postgres itself only offers transactions (gone once committed) and point-in-time recovery (which rewinds every user's work too). StitchDB records every row and schema change an agent key makes per session, so you can preview and revert one agent session in Studio or with one API call, and leave all other writes alone. This guide walks through that, plus the other undo tools for changes that did not come from an agent.

#Why "just restore a backup" is the wrong tool

Say an agent ran a cleanup at 14:02 that set status = 'archived' on 3,000 orders it should not have touched. By the time you notice at 14:40, customers have placed new orders and support has edited others. Your options in plain Postgres:

OptionProblem
Point-in-time recovery to 14:01Throws away 38 minutes of everyone's writes
Restore a copy and diff by handSlow, error-prone, and you must know which columns changed
Ask the agent to "undo it"It guesses the previous values; it does not have them

What you want is the inverse of exactly the agent's statements, applied only where nobody has changed the rows since. That needs recording at write time.

#How agent sessions are recorded

Every write made with an agent key belongs to a session:

  • With MCP, each MCP session is an undo session.
  • With the SQL API, the X-Stitch-Session header you send names it.
  • Without either, writes are grouped into one automatic session per hour. Direct connections also get one session per hour.

For each session StitchDB records:

  • Rows: inserted, updated and deleted rows, with old and new values. Tables need a primary key so rows can be matched back.
  • Schema: tables, indexes, views, functions, policies, constraints and columns the agent created or changed, together with the SQL that reverses them.
  • Drops: dropped tables go to a trash schema instead of being deleted, and dropped columns keep their values, so undo can bring them back.

Recording is on only while the project has agent keys that can write, and recorded changes count toward your database size until the undo window ends.

#Name sessions after the task

The single most useful habit: give each unit of work its own session name, such as the ticket, pull request or chat it came from.

Terminal
curl https://stitchdb.com/platform/v1/agent/sql \
  -H "Authorization: Bearer sdb_agt_..." \
  -H "X-Stitch-Session: fix-signup-bug" \
  -H "Content-Type: application/json" \
  -d '{"query": "update profiles set plan = 'pro' where id = 42"}'

In the JavaScript SDK, pass session to sql():

JavaScript
import { createAgentClient } from 'stitchdb-js/management'

const agent = createAgentClient({ agentKey: process.env.STITCHDB_AGENT_KEY })
const { data, error } = await agent.sql("update orders set status = 'shipped' where id = 42", { session: 'ticket-881' })

Then "undo ticket-881" means exactly that task, not "everything the bot did this hour".

#Undo a session in Studio

  1. Open AI agents → Sessions & undo and pick the session. The timeline shows each statement, its verdict and the rows it changed.
  2. Choose Undo session…. A dry run runs first and shows how many changes can be reversed and which conflict.
  3. Review the conflicts. A conflict is a row someone else changed after the agent. It is skipped, so a customer's later edit is never overwritten by the agent's "before" value.
  4. Confirm. Row changes are reversed and schema changes are undone with their recorded inverse SQL.

Use the Query log next to it to see what led there: every statement with its result (ran, blocked, needs approval, error), rows changed and duration.

#Undo with the API

The same flow, for scripts and incident runbooks:

Terminal
POST /platform/v1/projects/<ref>/agent-sessions/<session>/revert   {"dry_run": true}
POST /platform/v1/projects/<ref>/agent-sessions/<session>/revert   {"dry_run": false}
# the agent itself, for its own sessions:
POST /platform/v1/agent/sessions/<session>/revert                    {"dry_run": false}

All paths are under https://stitchdb.com/platform/v1. A dry run needs the developer role; the real undo needs an admin. The agent can also revert its own sessions, either with the undo_session MCP tool or with the SDK:

JavaScript
await agent.revert('ticket-881', { dry_run: false })   // undo everything the session changed

revert defaults to a dry run unless you pass dry_run: false, which is the right default for something an AI decides to call. A good instruction for an agent is: "If a change was wrong, call undo_session with a dry run, show me the result, and wait."

#What undo cannot reverse

Undo reverses changes inside the database. It cannot reach:

  • E-mails that were sent, push notifications that went out.
  • HTTP calls made from SQL with pg_net, or webhooks already delivered.
  • Queue messages that a worker already consumed.
  • Anything an Edge Function or external system did in response to the change.

This is a reason to keep side effects out of agent-driven writes: let triggers enqueue work, review it, then process it. The firewall also refuses HTTP from SQL for agent keys, which removes one class of side effect.

#Limits on how far back you can go

PlanUndo windowRows recorded per session
Free24 hours10,000
Pro7 days100,000
Scale14 days1,000,000
Enterprise30 days10,000,000

Values from the plans API as of November 2026 (agent limits, pricing). When a session records more rows than the plan keeps, recording stops for that session, it is marked capped, and only the changes recorded so far can be undone. That is one more reason to keep a low max rows per statement on agent keys: a statement over the limit is rolled back and sent for approval instead of running.

#When the change did not come from an agent

Agent sessions only cover agent keys. For other mistakes, StitchDB has three more tools, each with a different reach.

#Change history for one table

Turn on Track changes for a table in Database › Change history, and every insert, update and delete is recorded with who made it, the row before and the new values, whether it came from your app, Studio, an Edge Function or SQL. You can undo a single change:

SQL
select id, op, old, new, changed_at, changed_by
from _stitch_history.changes
where tbl = 'public.orders'::regclass and pk = '{"id": 42}'
order by id desc;

select _stitch_history.revert(1234);  -- 'reverted', 'gone' or 'exists'

An undo is itself recorded, so it can be undone too. Only changes after tracking was turned on are kept, truncate is not recorded, and history is kept for 1, 7, 30 or 90 days by plan (change history).

#The recycle bin for deleted rows

With the recycle bin on for a table, every deleted row is kept for 1, 7, 14 or 30 days by plan, with who deleted it:

SQL
select _stitch_bin.restore(array[41, 42]);

#Restore one table, or point in time

For a bad bulk update on a table without tracking, or a dropped table, Restore one table loads the table as it was at a moment in your point-in-time window into the _stitch_restore schema, next to the live one. You can then copy rows back yourself, put old rows back for rows matching a condition, or replace all rows. Full point-in-time recovery rewinds the whole database to a second within the last 1, 7, 14 or 30 days by plan, and loses everything after that moment. See backups and the point-in-time recovery guide.

#Which tool to reach for

What happenedUse
An agent key made bad changesAgent session undo
One row was changed wrongly by anyoneChange history (if tracking was on)
Rows were deletedRecycle bin, or change history
A table was dropped or mass-updated, no trackingRestore one table
Everything is brokenPoint-in-time recovery

#Prevent the next one

Undo is the last layer, not the first. Combine it with:

  • Read-only keys for agents that only need to look.
  • Approvals for writes without WHERE, big changes, drops and policy changes, so the dangerous statement waits for a person.
  • Denied tables for data the agent has no business touching.

Letting AI agents write to your database safely covers those settings.

#FAQ

#Does undo overwrite changes my users made after the agent?

No. A row someone else changed after the agent is reported as a conflict and skipped. The dry run lists them first.

#Can I undo a migration an agent applied?

Yes, if it was made with an agent key in the undo window. Schema changes are recorded with the SQL that reverses them, and dropped tables and columns are kept so they can come back.

#Are changes over a direct connection recorded?

Yes. Changes from an agent key's direct connection are recorded for undo, grouped into one session per hour, although the firewall and query log do not apply to that path.

#Does recording slow down writes?

Each recorded change is an extra write, and recorded changes use database space until they expire. Recording is on only while the project has agent keys that can write.

Give your agents a way back: create a free project and read sessions and undo.