REST API Reference Generated by AI

Alongside the MCP server, MockMotor exposes the same environment/service/reaction/account management as a plain REST API under /console/api/ — JSON over HTTP, no JSON-RPC envelope required, for scripts, CI pipelines, or any HTTP client that doesn't speak MCP. Every endpoint here calls the exact same server-side logic as its MCP tool counterpart (the same *Handler classes), so the two stay behaviourally identical — only the transport differs.

This page pulls its field-level documentation live from the MCP server's own tools/list response (see MCP Tools Reference), so parameter and result descriptions always match what the server actually validates — only the HTTP method/path framing below is REST-specific.

Conventions used by every endpoint below
  • Auth: read-only endpoints (GET) work without authentication. Endpoints that change something require an API key, sent as either the Authorization: Bearer <key> or X-MockMotor-API-Key: <key> header — find your key on your account page.
  • GET/DELETE parameters go in the query string, e.g. ?envid=XXX&varid=YYY.
  • POST parameters go in a JSON request body (Content-Type: application/json) — one flat JSON object with the fields listed under "Body" below, same field names as the equivalent MCP tool's arguments.
  • Create vs. update share one endpoint for several resources (environment, service, reaction, property, account): POST without the resource's id field creates a new one; POST with it updates the existing one. Both variants are documented below where this applies.
  • Errors always come back as HTTP 200 with a {"error": "message"} body (matching the rest of the MockMotor console) — check for an error key rather than the HTTP status code.

Lessons learned

Field notes from real agent sessions against this server — things that aren't obvious from the schema alone. These apply the same whether you're calling the REST endpoints below or the equivalent MCP tools, since both go through the same server-side logic.

Authentication failure modes are distinguishable — use them to diagnose
  • "Authentication required. Pass the X-MockMotor-API-Key header." means the key itself is missing/invalid — a header-format problem is not the likely cause if you already sent X-MockMotor-API-Key: or Authorization: Bearer; the key value itself is wrong or stale.
  • "Access denied: you are not an owner of this environment." means the key is valid and authenticates correctly, but that user isn't in the environment's owner list. Check GET /console/api/owners.
  • Read-only endpoints (GET) succeed even fully unauthenticated, so a successful read does not confirm a key works — only a write call (e.g. POST /console/api/reaction) proves authentication.
  • User accounts can be recreated with a new internal ID over time, silently dropping them from an environment's owner list even though the username is unchanged. If a previously-working key suddenly gets "Access denied," re-check the owners list before assuming the key is wrong.
Don't use JS dot-notation on input for XML/SOAP bodies

For SOAP/XML payloads, use XQuery instead: $input//*:fieldName (wildcard-namespace match) reliably extracts fields regardless of the request's namespace prefixes, e.g. $input//*:ban[starts-with(.,'6')].

The $accounts variable is a single wrapper element, not a sequence

In a payload/response script it has the shape <accounts><account>...</account><account>...</account></accounts>. Iterate with for $a in $accounts/account, not for $a in $accounts (the latter silently loops exactly once, over the wrapper). Each <account> exposes its properties as direct child elements ($a/BAN, $a/SUB, ...) plus an <ID> element. This differs from the singular $account bound per-candidate inside accountsSelectorScript during selection.

maxAccounts defaults to 1

If a reaction needs to bind more than one matching account (e.g. multiple subscriber rows per key), explicitly raise maxAccounts, or only the first match is ever used regardless of how many accounts actually match.

Prefer accounts-driven matching over a hardcoded matchByScript whitelist

...when a reaction's response is generated from mock account data. A hardcoded OR-chain of known keys requires editing the reaction every time mock data changes, and silently drifts out of sync (new accounts added via create_account won't be served until the reaction is manually updated). Instead:

  • Leave matchByScript empty; rely on matchByFirstElement/matchByRelativeURI for base routing.
  • Use accountsSelectorScript to select by key, e.g. $account/BAN = $input//*:ban.
  • Set ifAccountNotFound: skip so the reaction only fires when a matching account exists, falling through to the next reaction (e.g. a default stub) otherwise.
  • For a "known key, legitimately zero results" case, use a real sentinel account (key present, other fields blank) plus a payload filter (e.g. $accounts/account[normalize-space(SUB) != '']), rather than encoding that case into the match script.
POST /console/api/reactionDebug is the primary diagnostic tool

It returns a step-by-step trace (Match by HTTP Method, Match by FirstPayloadElement, Script XQ/Script JS, Match by Script, Executed/Skipped reaction) that pinpoints exactly which stage failed and why. When a variable's structure is unclear, dump it directly as a temporary diagnostic payload (e.g. <Debug>{ for $a in $accounts return <acc>{$a}</acc> }</Debug>) rather than guessing.

New reactions are inserted first in evaluation order

Creating a reaction places it before all existing reactions. This is useful for overriding a generated default, but means an overly broad matcher will shadow every request matching its base criteria — scope it precisely with matchByScript/accountsSelectorScript rather than relying on order alone.

Loading field documentation from /console/api/mcp