LVNDRby Toolfora

HelpRunning agents safely

Running agents safely

LVNDR does not sit between an agent and your store. The agent writes with its own tools: the Admin API, the Shopify CLI, a store MCP server, an app. LVNDR watches the store, records each change it tracks with who made it, and puts changes back. It records and reverses; it never blocks.

The pattern is always the same: start a run, let the agent act, review what the run changed, undo what is wrong.

1. Give each agent its own key

Make keys in Settings > Agents & API, one per agent or pipeline. A key is shown once and stored only as a hash. Every undo, restore point and export records the key that asked, and revoking one key leaves the others working. Agent access (REST API, MCP and CLI) is included on every plan.

A new key gets read and undo. Rollback and export are off until you tick them, so a key handed to an agent cannot reset the whole store or download the history unless you chose that.

ScopeAllowsIn Settings
readEvery read, every preview, marking a restore point, starting and ending runs. The same on MCP, REST and the CLI.Always on
undoUndoing one change, a batch or a run, and dismissing alerts (so a read-only agent cannot silence the alert its own burst raised). A run started with this scope captures every unattributed change while it is open.On by default
rollbackRolling back to a restore point (marking one needs only read)Off by default
exportDownloading the change log as CSV or JSONOff by default

A key made in Settings expires in 30, 90 (the default) or 365 days, or never. An agent connected through OAuth gets its own key, listed as “Connected via OAuth”, that lasts one hour and is refreshed by the client; a client that asks for no particular scope gets read and undo. Revoke either kind in Settings at any time. Uninstalling LVNDR revokes every key and every OAuth connection; after a reinstall, agents need new ones.

KeyGive it toWorst it can do through LVNDR
read onlyReporting agents, dashboards, anyone asking “what changed?”Read the history; mark restore points; open runs that capture only its own OAuth client's changes, or nothing
read + undoAgents that may fix their own mistakesUndo changes, batches and runs, each after a preview
read + undo + rollbackAn operator, or an agent you trust to reset the storeUndo everything after a restore point, including other people's changes
A read-only key cannot write to the store through LVNDR. It does not limit the agent's other tools: its Shopify token or the Shopify CLI can still change the store. LVNDR records those changes and can undo them.

2. Start a run

A run is one piece of agent work: an agent name, a task, usually a restore point, and every change tagged to it. Undoing a run reverts exactly its changes, newest first, and leaves everything else alone.

StepMCPCLIREST
Startlvndr_start_run (marks a restore point unless markRestorePoint is false)lvndr run start --agent <name> --task <text>POST /api/v1/runs (marks a restore point unless markRestorePoint is false)
Actyour Shopify toolyour Shopify toolyour Shopify tool
Endlvndr_end_runlvndr run endPOST /api/v1/runs/:id/end
Reviewlvndr_get_run every ~20 s until attributedThrough is after the last write and pendingDeliveries is 0lvndr run show (guard waits for you)GET /api/v1/runs/:id, polled the same way
Undolvndr_preview_undo_run, then lvndr_undo_run with its token, then lvndr_get_job until it finisheslvndr run undo, then --run --plan-hash <hash> (waits for the job)POST /api/v1/runs/:id/undo dry run, then dryRun false with its planHash (202 with a job), then GET /api/v1/jobs/:id

Starting a run and marking a restore point need only the read scope, on every surface.

Which changes join a run

The start response says what the run will capture, in capture and a one-line captureNote:

captureWhenWhat joins the run
all-unattributedThe key has the undo scopeEvery change whose actor is unknown or only inferred, staff edits included, while the run is open. Field edits are inferred, so most edits by an agent land here.
clientAn OAuth token without undoOnly changes linked to the token's own OAuth client
noneA Settings key without undoNothing. Review the work with a search by time instead. The start response then carries a warning that says so.

Expect the unknown tier often. Shopify names nobody for a plain field edit, so a price or title change by an agent, an app or a person arrives with no actor at all. LVNDR labels it “Not recorded” (tier unknown) until it can guess (inferred), and an open catch-all run adopts it as the run's, still marked inferred. Treat unknown as “no evidence either way”, never as “nobody”. LVNDR's own undos are never unknown: they are labelled “LVNDR undo” the moment Shopify reports them and never join a run or a batch.

  • A clientId passed when starting the run is used only if it is the key's own OAuth client id; otherwise it is ignored and captureNote says so. A read-only key therefore cannot sweep other people's edits into a run that someone may later undo as one.
  • Only changes inside the run's window count: from one second before it started until it ended. A run left open stops collecting after 24 hours.
  • lvndr's own undos never join a run. A change is tagged once and never moved to another run.
  • Tagging happens at attribution time, within about 4 minutes of the change. Collection rules and SEO, menus, themes and metafield definitions come from a 10-minute check, image alt text from the daily sweep. Before reviewing, poll the run (lvndr_get_run or GET /api/v1/runs/:id) about every 20 seconds until attributedThrough is after the last write and pendingDeliveries is 0.

Name the agent. The CLI sends --agent or LVNDR_AGENT; any HTTP client can send X-Lvndr-Agent; MCP uses the client's own name. Writes made through LVNDR with X-Lvndr-Run-Id (or runId in an MCP tool) are recorded with that run.

3. Guard: a run around any command

lvndr guard starts a run with a restore point, runs your command with LVNDR_RUN_ID and LVNDR_AGENT set, ends the run, polls the run every 20 seconds until it is attributed (at most --wait seconds, default 300), lists what changed and prints the undo preview line. It exits with the command's exit code. If the run cannot be started, the command does not run.

lvndr guard --agent price-bot -- shopify store execute --allow-mutations \
  --store example.myshopify.com --query-file raise-prices.graphql

See CLI for every flag.

4. Review and undo in the app

Agent runs in the app's navigation lists every run, newest first, with its agent, task, change count and restore point. A run's page shows each tagged change and these actions.

  • Preview undo of this run: plans the newest 50 changes without writing and says how many would be undone, how many left as they are, and how many more it did not check.
  • Undo N changes… (the same button once the preview is shown) asks you to confirm in a banner; “Yes, undo them” starts a background job that writes newest first, snapshotting each record first, with a progress banner on the page. Fields changed again since are left alone. One job covers up to 500 changes. If changes joined the run after the preview, the job does not start; preview again.
  • End run: closes an open run so later changes do not join it.

Rolling back to the run's restore point also undoes changes made by others after it. Undoing the run touches only the run's own changes. Prefer the narrowest undo that fixes the problem.

5. Agents always preview before they write

SurfaceHow a write is confirmed
MCPEach store write has a preview tool that returns a confirmation_token, valid 10 minutes and single use, for every kind of key. The write tool refuses without it, and refuses if the store or the set of changes moved since the preview.
CLIEvery undo is a preview, which prints the --run --plan-hash command that writes exactly what it showed. --run without --plan-hash is refused. rollback needs --confirm and --plan-hash. --yes previews, asks Apply? [y/N] and writes, only for a person at a terminal.
RESTUndo routes default to dryRun: true and return planHash. A write without planHash is refused with 400 plan_required, a stale one with 409 plan_changed. A rollback also needs confirm: true. Unknown body fields are refused with 400 unknown_field.
AppEvery undo, batch undo, run undo and rollback shows a preview page first.

Details in MCP and REST API.

Sidekick

Merchants can ask Shopify Sidekick about their change history: “What changed in the last hour?”, “Who changed the Snowboard product?”, “What did the last agent run do?”. Sidekick answers from LVNDR's data with links into the app.

The Sidekick extension only reads. Its seven tools cover recent changes, a record's history, one change, restore points, open alerts, agent runs and coverage. An answer about undoing links to the undo preview in LVNDR, and the merchant confirms there. Sidekick is not available on every store.

Alert webhook

When one actor changes more records than your threshold in the window, LVNDR raises an alert (see Alerts). Agent actors trip it at half the threshold, never below 3. Under Settings > Treat these as agents you can add your own app titles or API client ids.

Set an https URL under Settings > Alert webhook and every new or growing alert is POSTed there as JSON. Settings shows a signing secret; “Generate a new secret” rotates it. Save an empty URL to turn the webhook off.

The URL must be https on a public host. Addresses on a private network, loopback or link-local, and URLs with a user name or password, are refused when you save. The host is resolved again on every send and the connection goes to the checked address only.

HeaderValue
Content-Typeapplication/json
User-Agentlvndr-webhooks/1
X-Lvndr-Eventalert.burst
X-Lvndr-Signaturesha256=<hex HMAC-SHA256 of the raw body with the secret>
{
  "type": "alert.burst",
  "shop": "example.myshopify.com",
  "alert": {
    "id": "cm9al...", "kind": "burst", "actorKey": "...", "actorLabel": "price-bot",
    "count": 42, "windowStart": "2026-11-13T10:00:00.000Z", "windowEnd": "2026-11-13T10:04:10.000Z",
    "anchorChangeId": "cm9ch...", "resourceTypes": ["PRODUCT"], "status": "open",
    "createdAt": "2026-11-13T10:05:00.000Z"
  },
  "batchUrl": "https://admin.shopify.com/store/example/apps/.../app/batches/cm9ch...",
  "sentAt": "2026-11-13T10:05:01.000Z"
}

Verify the signature over the raw body, in constant time, before trusting the payload:

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}

LVNDR waits 5 seconds for an answer and does not follow redirects. Any 2xx counts as delivered. A failure is recorded on the alert as a short reason (“delivery failed (status 500)”, “unreachable”, “timed out after 5s”) and does not stop the banner or the email. A receiver can page someone, post to chat, or list the burst with lvndr batch <anchorChangeId>.

What LVNDR cannot undo

  • Themes. They are tracked, but Shopify lets only exempted apps write theme files. The preview shows the earlier value for a manual restore.
  • Orders, customers and other protected data. They are not read at all.
  • A deleted record comes back with a new id. A rebuilt product has no media, inventory or publication; a rebuilt blog has no posts.
  • Lists longer than one snapshot page are flagged truncated and are not undoable.
  • Changes older than your plan's history window.
  • Fields LVNDR cannot write show as unsupported in the preview, with the earlier value. See Coverage.
  • Fields changed again after the change show as conflict and are left alone unless you choose to overwrite them.
  • Anything outside Shopify: emails sent, orders placed, syncs to other systems while the bad data was live.

See Coverage for the full list per record type.