# Undo my change

URL: /agents/undo-my-change · Id: `guide:undo-my-change`

> Revert the newest Change you merged to the definition, such as a price or a page, as a new Change you can revert in turn.

[`Change.revert`](/business/nouns/change/revert) puts back every value your newest merged [Change](/business/nouns/change) set, as a new Change the [Version](/business/nouns/version) history keeps, and your agent reads it back first. To reverse an agent's [Action](/business/nouns/action) instead, [undo what the agent did](/agents/undo-what-the-agent-did).

**Before it runs**

- You leave the chat: Never.

## What you get
- 1 reverting Change that puts back every value your Change set (Change)
- 1 new Version of the Startup, with the reverted one kept in its history (Version)

## Your agent does
**For agents**

- Guide: `guide:undo-my-change`. Page: [/agents/undo-my-change.md](/agents/undo-my-change.md).
- Approval: none: only the Startup’s own records change. Run it when the founder asks.
- Leaves the chat: never.
- Run: call the steps in order, each through the API, CLI, MCP or SDK tab below; a later step passes the `$id` an earlier one returns. Each value marked example is the sample business’s: pass the founder’s own.
- Retry: each write sends the Idempotency-Key `undo-my-change-<the Startup's domain>-<run>-<step>` (`undo-my-change-leaseline.app-$RUN-1` for step 1 of the worked example), in the API and CLI as a header or flag and in the SDK as `{ idempotencyKey }`. Mint `run` once per founder request (`RUN=$(uuidgen)`, `crypto.randomUUID()`): the same ask made again later is a new request with a new run, so it runs again. After a timeout, rerun with the same run: api.sb returns each finished step's first Action and runs nothing twice.
- Retry in MCP: the `do` call carries `idempotencyKey: "undo-my-change-<the Startup's domain>-<run>"`, and each write inside sends it plus `-<n>`, its call number. Rerun the same code with the same key after a timeout: finished calls return their first Action, and only the rest run.
- Lookups: a `find: … newest` reads the newest match, and the agent reads it back to the founder before the step runs.

| Step | Verb | Inputs (type, required, this guide’s value) | Returns |
| --- | --- | --- | --- |
| 1 | [`Change.revert`](/business/nouns/change/revert) (`verb:Change.revert`) | `change` Change (an id), required: the `$id` of the newest Change `Change.list` returns for `where: status = Merged`, read back to the founder before the step; `reason` text, required: example `"Changed by mistake."` | `revert` Change |

Returns: the last call answers with `revert` and its `action`, whose `status` is `done`, `pending` (waiting on an Approval or a Worker), `failed`, `canceled` or `refused`:

```json
{
  "api": "https://api.sb/leaseline.app",
  "revert": {
    "$id": "https://api.sb/leaseline.app/changes/change_3Ex8uVSOyQ",
    "$type": "Change"
  },
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/act_br07HsG8MG",
    "operation": "Change.revert",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/act_br07HsG8MG"
  }
}
```

Next: offer “Revert only the price, keep the new name”, or “Show me what I changed this week”.

Calls: `Change.revert`

Ask:

```text
Undo the price change I made yesterday.
Put back what I changed.
Revert my last change.
```

API:

```bash
# Look up the Change: Change.list
RUN=$(uuidgen)  # once per request; reuse it only to retry this request
CHANGE_MATCHES=$(curl "https://api.sb/leaseline.app/changes?where=status%20%3D%20Merged&limit=1" \
  -H "Authorization: Bearer $SB_TOKEN")
[ "$(echo "$CHANGE_MATCHES" | jq '.changes | length')" = 1 ] || { echo "No Change matches: tell the founder"; exit 1; }
CHANGE=$(echo "$CHANGE_MATCHES" | jq -r '.changes[0]["$id"] | split("/") | last')

# Change.revert
curl -X POST https://api.sb/leaseline.app/changes/$CHANGE/revert \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Idempotency-Key: undo-my-change-leaseline.app-$RUN-1" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Changed by mistake."}'
```

CLI:

```bash
RUN=$(uuidgen)  # once per request; reuse it only to retry this request
CHANGE_MATCHES=$(npx startup-builder do Change.list --data '{"where":"status = Merged","limit":1}' --json)
[ "$(echo "$CHANGE_MATCHES" | jq '.changes | length')" = 1 ] || { echo "No Change matches: tell the founder"; exit 1; }
CHANGE=$(echo "$CHANGE_MATCHES" | jq -r '.changes[0]["$id"] | split("/") | last')
npx startup-builder do Change.revert --data '{"change":"'"$CHANGE"'","reason":"Changed by mistake."}' --idempotency-key undo-my-change-leaseline.app-$RUN-1
```

MCP:

```json
{
  "name": "do",
  "arguments": {
    "code": "const [change] = await $.Change.list({ where: 'status = Merged', limit: 1 })\nif (!change) throw new Error('No Change matches: tell the founder')\nreturn await $.Change.revert({ change: change.$id, reason: 'Changed by mistake.' })",
    "idempotencyKey": "undo-my-change-leaseline.app-<run>"
  }
}
```

SDK:

```ts
import { $ } from 'startup-builder'

const run = crypto.randomUUID() // once per request; reuse it only to retry this request
const [change] = await $.Change.list({ where: 'status = Merged', limit: 1 })
if (!change) throw new Error('No Change matches: tell the founder')
await $.Change.revert({ change: change.$id, reason: 'Changed by mistake.' }, { idempotencyKey: `undo-my-change-leaseline.app-${run}-1` })
```

## Change it
- Revert only the price, keep the new name.
- Show me what I changed this week.
- Undo what the agent did instead.

## Under the hood
- Systems: [Work queue](/agents/work-queue)
- Nouns: [Change](/business/nouns/change), [Version](/business/nouns/version)
- Events: [`Change.reverted`](/business/events/Change.reverted)