# How changes work

Every change in Usual (a skip, a new offer, a cancellation, a setting) goes through one path, whether it comes from the admin, a customer, the API or an agent. That path is what makes it safe to hand to software.

## Commands

A *command* is one kind of change: `subscriptions.skip_next`, `offers.create`, `shop.update_settings`. Each one declares:

- **its input**, as a JSON schema, checked before anything happens;
- **who may run it**: staff, API keys, agents, customers;
- **the permission it needs**, like `subscriptions:write`;
- **its risk**: `low`, `medium`, `high` or `financial`;
- **whether it can be undone**.

`GET /commands` (or `list_commands` in MCP) returns every command your key may run, with all of the above. The full list is in [Commands](/docs/reference/commands).

## Plans

Before a command changes anything, it works out a *plan*: a plain-language summary, the exact fields that would change from what to what, and how many records it touches.

```json
{
  "diff": {
    "summary": "Deliver every 2 weeks instead of every month. The next charge date stays the same.",
    "changes": [{ "path": "billing", "from": { "every": 1, "interval": "MONTH" }, "to": { "every": 2, "interval": "WEEK" } }]
  },
  "affected": 1
}
```

You get a plan back when you:

- add `?preview=true` to a command's endpoint, or call `POST /plans`;
- use an **agent** key: agents always get a plan first, whatever they ask for;
- make a change that needs a person's approval.

Apply it with `POST /plans/{id}/apply`. Plans are applied **exactly as previewed**: if the subscription changed since (say the customer skipped in the meantime), the plan is refused as `plan_stale` instead of doing something nobody saw. Plans expire after 15 minutes; make a new one.

If nothing would change, you get `"status": "noop"` and nothing is recorded.

## Approvals

Some changes wait for a person to approve them in **Usual → Activity**. Built in, and merchants can only make these stricter:

| Who | Needs approval for |
|---|---|
| Agents | Any `medium`, `high` or `financial` risk change, or any change touching more than 25 records |
| API keys | `financial` changes, like retrying a charge |
| Staff in the admin | Nothing: they're the approvers |

A plan waiting for approval has `"status": "awaiting_approval"` and lasts 72 hours. Check it with `GET /plans/{id}`; once approved, apply it as usual.

Agents must also say **why** with every change (`X-Change-Reason` header, or `reason` in MCP). It's recorded with the change and shown to the person approving it.

## Undo

Every applied change is recorded in the activity log: what changed, from what to what, who did it (and for agents, on whose behalf), why, and when. `GET /activity` lists them.

If a command is reversible, its record carries its own inverse. `POST /activity/{id}/undo` runs that inverse as a new change, through the same checks (so an agent's undo is a plan too). A change can only be undone once.

## Who did it

Each change records one of these as its actor, and it's shown everywhere the change appears:

| Actor | Is |
|---|---|
| `merchant_user` | Someone in the store's Shopify admin |
| `api_key` | An integration using an API key |
| `merchant_agent` | An AI agent using an agent key |
| `customer` | The subscriber, from their account page or the store's portal |
| `system` | Usual itself: renewals, retries, syncing from Shopify |
