# ShoppingMaster admin API — reference for the MCP admin API tools

The tools `admin_api_read`, `admin_api_create`, `admin_api_update` and
`admin_api_delete` call the store admin's own HTTP API as the connected person:
their store, their role, the same validations, history, emails and webhooks as
the admin. The HTTP method follows from the tool and the path (no method
argument). Every path is canonical: lowercase router prefix, no trailing slash,
no empty, `.` or `..` segments, no percent-encoding except `%2F` inside a
variants collection (`products%2F<id>%2Fvariants`), no query string in the path.

## Reading — admin_api_read

| Path | Wire method | Body / query |
|---|---|---|
| `/api/db/<collection>/<id>` | GET | `query`: optional parameters |
| `/api/db/<collection>/query` | POST | `{ filters: [[field, op, value], …], orderBy: [field, "asc"\|"desc"], limit }` |
| `/api/db/<collection>/count` | POST | `{ filters }` |
| `/api/db/products/category-counts` | POST | `{ filters }` |

Filter operators: `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `array-contains`.
Fields may be dotted paths (`settings.currency`). Queries of store data are
always narrowed to the connected store (a `shopId` filter for another store
is replaced). Any collection the person can read in the admin can be read.

## Writing — admin_api_create, admin_api_update, admin_api_delete

| Tool | Path | Wire method | Body |
|---|---|---|---|
| admin_api_create | `/api/db/<collection>` | POST | the record (its `shopId` is the connected store's) |
| admin_api_update | `/api/db/<collection>/<id>` | PATCH | `{ updates: { field: value, "a.b": value } }` — merges those fields |
| admin_api_update | `/api/db/<collection>/<id>` | PUT | `{ data: { … }, merge? }` — replaces the document |
| admin_api_delete | `/api/db/<collection>/<id>` | DELETE | none |
| each, by op type | `/api/db/batch` | POST | `{ ops: [{ type, collection, docId?, data?, updates? }] }` |

Write tools answer a preview of the exact request unless called with
`confirm: true`. A PATCH or PUT may carry `ifUpdatedAt` (the document's
`updatedAt` as read): a document changed since then is refused with 409
`conflict`.

### What each collection takes (deny by default: anything not listed is refused)

| Collection | POST (admin_api_create) | PATCH (admin_api_update) | PUT (admin_api_update) | DELETE (admin_api_delete) |
|---|---|---|---|---|
| `products` | any field | any field | — | any field |
| `products%2F<product id>%2Fvariants` | any field | any field | any field | any field |
| `categories` | any field | any field | — | any field |
| `collections` | any field | any field | — | any field |
| `pages` | any field | any field | — | any field |
| `articles` | any field | any field | — | any field |
| `menus` | any field | any field | — | any field |
| `themes` | any field | any field | any field | any field |
| `coupons` | any field | any field | — | any field |
| `automaticDiscounts` | any field | any field | — | any field |
| `customerProfiles` | — | any field | any field | any field |
| `webhooks` | any field | any field | — | any field |
| `reviews` | — | `approved`, `updatedAt` | — | any field |
| `subscribers` | — | `status`, `updatedAt` | — | any field |
| `stockAlerts` | — | — | — | any field |
| `orders` | — | `note`, `tags`, `timelineComments`, `customerInfo`, `trackingNumber`, `trackingCompany`, `trackingUrl`, `updatedAt` | — | — |
| `giftcards` | — | `active`, `note`, `updatedAt` | — | — |
| `shops` | — | `updatedAt`, `settings.name`, `settings.title`, `settings.email`, `settings.phone`, `settings.address`, `settings.logo`, `settings.colors`, `settings.banner`, `settings.social`, `settings.announcement`, `settings.meta`, `settings.outOfStockMessage`, `settings.checkout`, `settings.marketing`, `settings.orderLookup`, `settings.showWhatsAppButton`, `settings.orderNotificationEmail`, `settings.currencies`, `settings.unitSystem`, `settings.weightUnit`, `settings.timezone`, `settings.language`, `settings.theme`, `settings.publishedThemeId` | — | — |

Batches: only `products`, `variants`, `categories`, `collections`, `pages`, `articles`, `menus`, `themes`; op types `add`
(admin_api_create), `update` / `set` (admin_api_update), `delete`
(admin_api_delete); every op is checked against the table above.

Never reachable: orders' money and status fields, refunds, payment status,
totals and lines, gift card creation, balances and redemptions, payment /
shipping / tax / location / group / domain settings, the plan, users,
returns, purchases, sign-in, sessions, tokens, passwords, OAuth,
impersonation, API keys, MCP, SSL and billing. Their own tools exist for the
store operations among them. No agent tool moves money: refunds and gift
card value are done by the person in the admin.

## Routes outside the data API

| Tool | Request | Body | What it does |
|---|---|---|---|
| admin_api_read | POST `/api/orders/recompute` | `{ order }` | the authoritative total of an order as edited (the admin's order editor) |
| admin_api_read | POST `/api/email/preview` | `{ … the email }` | the rendered preview of a store email |
| admin_api_read | GET `/api/email/sender` | — | who the store emails come from |
| admin_api_read | GET `/api/mercadolibre/status` | — | the Mercado Libre connection and last sync |
| admin_api_create | POST `/api/orders/<draft order id>/send-invoice` | `{ language }` | emails a draft order's invoice (a link to pay it) to its customer |
| admin_api_create | POST `/api/giftcards/<card id>/send` | `{ email, name, message }` | emails a gift card to someone |
| admin_api_create | POST `/api/email/send` | `{ … the email }` | sends a store email |
| admin_api_update | POST `/api/checkouts/reconcile` | — | marks the abandoned checkouts that became orders as completed |
| admin_api_update | POST `/api/mercadolibre/sync` | — | syncs the catalog with Mercado Libre now |
| admin_api_update | PATCH `/api/mercadolibre/settings` | `{ … preferences }` | changes the Mercado Libre preferences |
| admin_api_delete | POST `/api/storage/delete` | `{ path }` | deletes a file of the store's storage |

## Answers

`{ status, ok, done? , body }` — `body` is the API's JSON (a list comes as
`{ items, count }`). Fields whose names look like secrets (token, secret,
password, hash, key, credential, refresh) come back as `[hidden]`. Answers over
60 kB are cut (`truncated_json`, with a note).
