HelpDevelopers
Built for stores run by agents
LVNDR records changes to a Shopify store's products, collections, pages, blogs and posts, menus, metafield definitions and theme settings, with who made them as far as Shopify says, and puts them back in one call (themes excepted). Connect your agent over MCP and it can announce what it is about to do, check its own work, and undo it when the result is wrong.
Works with Claude Code, Claude Desktop, Cursor, VS Code, Codex, the lvndr CLI and plain HTTPS. Free on development stores.
The safe workflow
- Install LVNDR on the store
Install the LVNDR app on the Shopify store and wait for its first snapshot (the change log shows the progress). Then connect your agent below and call
lvndr_statusto confirm which store it reached. - Start a run with a restore point
lvndr_start_runwith the agent name and the task. LVNDR marks a restore point and tags the changes that follow with the run. Which changes depends on the key: the answer'scapturesays so. - Act with your Shopify tool
Through the Shopify admin, the Admin API, a CLI or any other tool. LVNDR watches the store; nothing goes through it.
- End the run and wait for LVNDR to catch up
lvndr_end_runcloses it. Changes are tagged within about 4 minutes (collection rules and SEO, menus, themes and metafield definitions up to 10). Polllvndr_get_runabout every 20 seconds untilattributedThroughis after your last write andpendingDeliveriesis 0, then review what the run touched. - Preview, then undo
If the result is wrong,
lvndr_preview_undo_runshows the plan and returns a confirmation token;lvndr_undo_runwith the token puts every change back, newest first.lvndr_preview_undoandlvndr_undodo the same for one change. The merchant sees the same run in the app with the same buttons.
The whole workflow, with what LVNDR cannot undo, is in Running agents safely.
Connect your agent
The MCP endpoint is https://lvndr.toolfora.com/mcp. Clients that support MCP authorization need only that URL: the client opens a browser, the merchant names the store, approves inside their Shopify admin, and the client receives its own key: one hour, refreshed automatically, listed in Settings > Agents & API as “Connected via OAuth ” and revocable there. Details in MCP.
The second option, for clients without OAuth or for scripts, is an API key from the app: Settings > Agents & API > Create key. Replace lv_YOUR_KEY in the key snippets with it, or set it as LVNDR_API_KEY where the snippet says so.
Claude Code
Keyless (OAuth): add the server, then run /mcp in Claude Code to sign in
claude mcp add -t http lvndr https://lvndr.toolfora.com/mcpWith an API key
claude mcp add -t http -H "Authorization: Bearer lv_YOUR_KEY" lvndr https://lvndr.toolfora.com/mcpOr a project .mcp.json with the key from LVNDR_API_KEY (commit it)
{
"mcpServers": {
"lvndr": {
"type": "http",
"url": "https://lvndr.toolfora.com/mcp",
"headers": {
"Authorization": "Bearer ${LVNDR_API_KEY}"
}
}
}
}Cursor
Keyless (OAuth): .cursor/mcp.json or ~/.cursor/mcp.json; Cursor offers to log in
{
"mcpServers": {
"lvndr": {
"url": "https://lvndr.toolfora.com/mcp"
}
}
}Add to Cursor with a keySet LVNDR_API_KEY in your shell before starting Cursor.
Or .cursor/mcp.json with a key
{
"mcpServers": {
"lvndr": {
"url": "https://lvndr.toolfora.com/mcp",
"headers": {
"Authorization": "Bearer lv_YOUR_KEY"
}
}
}
}VS Code
Keyless (OAuth): .vscode/mcp.json; VS Code runs the sign-in
{
"servers": {
"lvndr": {
"type": "http",
"url": "https://lvndr.toolfora.com/mcp"
}
}
}Add to VS Code with a keyVS Code asks for the key the first time the server starts.
Or .vscode/mcp.json with a key
{
"inputs": [
{
"type": "promptString",
"id": "lvndr-key",
"description": "LVNDR API key",
"password": true
}
],
"servers": {
"lvndr": {
"type": "http",
"url": "https://lvndr.toolfora.com/mcp",
"headers": {
"Authorization": "Bearer ${input:lvndr-key}"
}
}
}
}Codex
~/.codex/config.toml
[mcp_servers.lvndr]
url = "https://lvndr.toolfora.com/mcp"
bearer_token_env_var = "LVNDR_API_KEY"Export LVNDR_API_KEY before starting Codex.
Claude Desktop
claude_desktop_config.json, via mcp-remote
{
"mcpServers": {
"lvndr": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://lvndr.toolfora.com/mcp",
"--header",
"Authorization: Bearer lv_YOUR_KEY"
]
}
}
}Settings > Developer > Edit Config, then restart Claude Desktop.
CLI
Environment
export LVNDR_API_KEY=lv_YOUR_KEY
export LVNDR_API_URL=https://lvndr.toolfora.comThen
npx lvndr status
npx lvndr changes --since 24hEvery command is listed in the CLI article.
REST
The newest five changes
curl -H "Authorization: Bearer lv_YOUR_KEY" https://lvndr.toolfora.com/api/v1/changes?limit=5- OpenAPI description: https://lvndr.toolfora.com/api/v1/openapi.json
- For language models: https://lvndr.toolfora.com/llms.txt
- Every route with examples: REST API
MCP tools
One Streamable HTTP endpoint, https://lvndr.toolfora.com/mcp, with an OAuth token or a key as the bearer. Results come back as text and as structuredContent. A tool that writes to the store needs a confirmation_token from its preview, for every kind of key: valid 10 minutes, single use, and refused if the store changed since the preview. Over the REST API the same holds for the dry run's planHash, and in the CLI for --plan-hash. The record is snapshotted first, so an undo can itself be undone.
Read
| Tool | Scope | What it does |
|---|---|---|
lvndr_status | read | Plan, tracked counts, last activity check and pipeline health. |
lvndr_search_changes | read | Changes newest first, filtered by time, type, record, actor, run or text. |
lvndr_get_change | read | One change with its full before/after diff. |
lvndr_record_history | read | Every version and change of one record, newest first. |
lvndr_get_batch | read | Everything the same actor did around a change (no gap over ten minutes). |
lvndr_list_restore_points | read | The moments the store can be rolled back to. |
lvndr_preview_undo | read | What undoing one change would write, field by field. Writes nothing; returns the confirmation_token for lvndr_undo. |
lvndr_preview_undo_run | read | What undoing a whole agent run would do, change by change. Writes nothing; returns the confirmation_token for lvndr_undo_run. |
lvndr_preview_undo_batch | read | What undoing one actor's batch would do. Writes nothing; returns the confirmation_token for lvndr_undo_batch. |
lvndr_preview_rollback | read | What rolling back to a restore point would undo. Writes nothing; returns the confirmation_token for lvndr_rollback. |
lvndr_list_alerts | read | Anomaly alerts, open ones by default: bursts of changes by one actor. |
lvndr_get_run | read | One agent run: who, what task, its restore point, its changes, and attributedThrough / pendingDeliveries to tell whether LVNDR has caught up. |
lvndr_list_runs | read | Recent agent runs, newest first; openOnly for runs left open. |
lvndr_get_job | read | Progress of a run undo, batch undo or rollback job. |
Write
| Tool | Scope | What it does |
|---|---|---|
lvndr_mark_restore_point | read | Marks a moment to roll back to. Writes only to LVNDR. |
lvndr_start_run | read | Announces a run: a restore point is marked and changes until the end are tagged with it. With the undo scope the run captures every unattributed change; without it, only the key's own OAuth client's changes, or none. |
lvndr_end_run | read | Closes the run and reports how many changes it made. |
lvndr_undo | undo | Puts one change back in the live store. Needs the confirmation_token from lvndr_preview_undo. |
lvndr_undo_run | undo | Undoes every change of a run, newest first, as a background job. Needs the confirmation_token from lvndr_preview_undo_run. |
lvndr_undo_batch | undo | Undoes a whole batch, newest first, as a background job. Needs the confirmation_token from lvndr_preview_undo_batch. |
lvndr_rollback | rollback | Undoes everything since a restore point, newest first. Needs the confirmation_token from lvndr_preview_rollback. |
lvndr_dismiss_alert | undo | Marks an alert as seen. Writes only to LVNDR, but needs Undo so a read-only key cannot silence its own alert. |
Key scopes
A key carries the scopes it was made with. Give an agent the least it needs; make another key when it needs more.
| Scope | Allows |
|---|---|
read | Every read tool and route, previews, marking restore points, starting and ending runs, dismissing alerts, on MCP, REST and the CLI alike. Always on. |
undo | Undoing one change, a run or a batch. A run started by a key with this scope captures every unattributed change while it is open; without it, a run captures only the key's own OAuth client's changes, or none. |
rollback | Rolling the whole store back to a restore point. |
export | Downloading the change log as CSV or JSON. |
A key made in Settings expires in 30, 90 (the default) or 365 days, or never, and can be revoked there at any time. Every write an agent makes through LVNDR is attributed to its key in the change log and the audit log. For changes an agent makes directly in the store, Shopify names the app only for lifecycle actions such as creating, publishing or deleting a record; field edits are inferred and labelled as such. The merchant can mark an app title or client id as an agent so it trips anomaly alerts sooner.
Rate limits
Per key, by the store's plan. Until managed billing is on, every store gets the Scale numbers.
| Plan | Reads per minute | Writes per minute |
|---|---|---|
| Starter | 60 | 10 |
| Growth | 120 | 20 |
| Scale | 300 | 60 |
| Development store (free) | 120 | 20 |
A request over the limit gets 429 with a Retry-After header. Over MCP, an OAuth token gets the same 429; a key made in Settings gets a tool result saying “Rate limited; retry in N s”. A path that does not exist costs nothing. Writes send an Idempotency-Key header to make a retry safe.
Alert webhook
Set a webhook URL in Settings > Alert webhook and LVNDR POSTs every anomaly alert to it as JSON, alongside the in-app banner and the email. Each request carries X-Lvndr-Signature: sha256=<hex>, the HMAC-SHA256 of the raw body with your webhook secret. Verify it 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);
}- Themes are tracked but never written: Shopify grants theme writes to a short list of apps. The undo preview shows the earlier value for a manual restore.
- Orders and customers are never read: they are protected data and out of scope by design.
- Field-level edits carry no staff name in Shopify's own records, so those actors are inferred, and labelled as such.
- Media is restored by reference or re-uploaded from its URL; inventory history is per location and quantity only.
- A deleted record comes back with a new id. A rebuilt product has no media, inventory or sales channels; a rebuilt blog has no posts.
More in the help articles: Running agents safely, MCP, REST API, CLI, what is tracked.