Skip to content

Using the REST API

The REST API is a thin HTTP adapter over the Mock Machines facade. It exposes scenarios and experiments as resources, so any HTTP client can browse a deployed scenario, run an experiment, and read back the results. It is a sibling of the MCP server — both drive the same Lab → Workbench → Scenario → Experiment facade, and neither holds engine logic of its own.

The live API is served at https://api.mockmachines.dev — no local build or server to run. Every endpoint below hangs off that base.

  • A liveness probe is at /health.
  • The MCP connector is the sibling adapter, served at https://mcp.mockmachines.dev.
  • The read-only studio is a browser UI over this same API — open a preset at https://app.mockmachines.dev/paymentprocessor.

The API is open for reading the curated preset scenarios anonymously — no token required. Creating, running, and deploying scenarios needs a bearer token, which scopes the work to your own private workbench.

TierTokenCan
AnonymousnoneList presets; read their pre-seeded experiments — entities, events, dataset, DuckDB bundle.
AuthenticatedAuthorization: Bearer <token>Everything above, plus deploy scenarios and create / run experiments in your own workbench.

Everything hangs off two nested resources:

  • A scenario is a deployed config. Deploys are additive — each POST /scenarios mints a new id, and the name is display-only. The curated presets have stable ids; the PaymentProcessor preset is paymentprocessor.
  • An experiment is one run of a scenario. It owns its dataset (entity tables, event log, exports) and is rehydrated from disk on demand, so an experiment is its on-disk dataset rather than a live in-process object. Each preset ships a pre-run default experiment, <scenario>-default — e.g. paymentprocessor-default.

The PaymentProcessor preset models a European payment processor — merchants onboard through verification and run recurring payments routed onto SEPA, BACS, and AUTOGIRO settlement schemes across eight linked tables. Its default experiment is pre-run, so you can read populated data straight away — no token:

Terminal window
API=https://api.mockmachines.dev
# The scenario's metadata, and its pre-run default experiment
curl -s $API/scenarios/paymentprocessor | jq
curl -s $API/scenarios/paymentprocessor/experiments/paymentprocessor-default | jq
# Entity types with counts, then all merchants, then one merchant
curl -s $API/scenarios/paymentprocessor/experiments/paymentprocessor-default/entities | jq
curl -s $API/scenarios/paymentprocessor/experiments/paymentprocessor-default/entities/Merchant | jq
curl -s $API/scenarios/paymentprocessor/experiments/paymentprocessor-default/entities/Merchant/Merchant-00000001 | jq
# The append-only event log, and the dataset manifest
curl -s "$API/scenarios/paymentprocessor/experiments/paymentprocessor-default/events?from=0&limit=20" | jq
curl -s $API/scenarios/paymentprocessor/experiments/paymentprocessor-default/dataset | jq
# Download the whole world as a DuckDB database + OSI semantic model (a zip)
curl -s -o paymentprocessor.zip \
$API/scenarios/paymentprocessor/experiments/paymentprocessor-default/db

Scenarios

MethodPathPurpose
GET/scenariosList deployed scenarios (presets included).
GET/scenarios/{id}One scenario’s metadata.
POST/scenariosDeploy a scenario from a YAML request body; returns the new id. (auth)
DELETE/scenarios/{id}Delete a scenario and all its experiments. (auth)

Experiments (under /scenarios/{id}/experiments)

MethodPathPurpose
GET/experimentsList a scenario’s experiments.
GET/experiments/{eid}An experiment’s status, turn, and entity counts — poll this after a run.
POST/experimentsCreate (seed) an experiment at turn 0. Body: {"seed_count": N, "event_log": bool, "decision_log": bool, "flushing": bool}. (auth)
DELETE/experiments/{eid}Delete an experiment. (auth)
POST/experiments/{eid}/runAdvance the experiment (see below). (auth)
POST/experiments/{eid}/persistCheckpoint at the current closed-turn boundary. (auth)
POST/experiments/{eid}/loadResume a named checkpoint into a new experiment. (auth)
POST/experiments/{eid}/exportExport tables to CSV or Parquet. (auth)
GET/experiments/{eid}/dbDownload the DuckDB database + OSI model as a zip.
GET/experiments/{eid}/eventsPaginated event log (?from=&limit=).
GET/experiments/{eid}/datasetDataset metadata (status, counts, file list).

Browsing a ready experiment (under .../experiments/{eid})

MethodPathPurpose
GET/entitiesMachine types with counts.
GET/entities/{type}All instances of a type, fields decoded.
GET/entities/{type}/{id}One instance’s field map.
GET/entities/{type}/{id}/actionsThe instance’s current state and available events, each with condition verdicts.

To run a fresh experiment you deploy (or reuse) a scenario in your own workbench and drive it with a bearer token. POST .../run with {"turns": N} starts a background run and returns 202 Accepted immediately — poll GET .../experiments/{eid} until its status is ready and current_turn reaches the target:

Terminal window
API=https://api.mockmachines.dev
AUTH="Authorization: Bearer $TOKEN"
# Deploy a config → seed → run → poll → read entities
SID=$(curl -s -H "$AUTH" -X POST $API/scenarios \
--data-binary @PaymentProcessor.yaml | jq -r .id)
EID=$(curl -s -H "$AUTH" -X POST $API/scenarios/$SID/experiments \
-d '{"seed_count":100}' | jq -r .id)
curl -s -H "$AUTH" -X POST $API/scenarios/$SID/experiments/$EID/run \
-d '{"turns":50}' # → 202 Accepted
until [ "$(curl -s -H "$AUTH" $API/scenarios/$SID/experiments/$EID | jq -r .status)" = ready ]; do
sleep 1
done
curl -s -H "$AUTH" $API/scenarios/$SID/experiments/$EID/entities | jq

Then export the tables, or pull the DuckDB + OSI bundle:

Terminal window
curl -s -H "$AUTH" -X POST $API/scenarios/$SID/experiments/$EID/export -d '{"format":"parquet"}'
curl -s -H "$AUTH" -o experiment.zip $API/scenarios/$SID/experiments/$EID/db

The same run endpoint can drive one entity for one step instead of N turns — pass a target and event rather than turns:

Terminal window
curl -H "$AUTH" -X POST $API/scenarios/$SID/experiments/$EID/run \
-d '{"target":{"type":"Merchant","id":"Merchant-00000001"},"event":"activate"}'

It returns the step outcome — fired, or stayed if the event’s conditions did not hold (a requested event is a no-op when masked, never a fall-through to a different event). Call .../entities/{type}/{id}/actions first to see which events are eligible.

Anonymous reads of the preset scenarios work out of the box. Authenticated requests present a bearer token in the Authorization: Bearer <token> header, verified as a Firebase ID token; the caller then operates in their own per-user workbench. The admin route POST /admin/reset-presets is separately gated (an MM_ADMIN_TOKEN bearer, a Firebase admin claim, or a scheduler OIDC token).

For longer sessions, the persist / load endpoints map to the checkpoint & resume flow.