Reference
Control plane reference
rystic help control-plane prints this reference for the CLI you have installed. Control plane covers what the routes are for and the features that unlock them. The text below is the CLI’s own, so a “world document” is a scenario file.
Every simulator serves its service’s API plus this control plane under
/_rystic/, on the same origin. GET /_rystic/version reports this
document’s version as contract.
Features. Each route is listed with the feature of the simulator’s
grant it needs, or as open on every plan when it needs none. A metered simulator
learns its grant from its lease (GET /_rystic/version,
lease.entitlements); a licensed one serves every route. A route the grant
lacks answers 403 with {message, feature, upgrade_url} before it reads
the method or the body. Two routes take no feature: /_rystic/activity is
same machine only, answering only a caller on the same machine, and
/_rystic/files/{path} takes a signed URL, its signature gating it in place
of a feature.
Routes some simulators lack. A few routes need a capability not every
simulator has, such as an evaluation engine, a console-set webhook or a
tape player; a simulator without it answers 404 there.
Methods. A route that changes state takes only the methods listed; any
other answers 405 with an Allow header, so a browser prefetch of
GET /_rystic/reset wipes nothing. Read routes take any method; GET is
documented. A path no route names answers 404
unknown control endpoint <path>.
Admin token. A simulator started with RYSTIC_ADMIN_TOKEN requires it on
every control route except /_rystic/version and the signed file URLs, as the
X-Rystic-Admin-Token header or the rystic_admin_token cookie: 401
without it, 403 with a wrong one (plain text). With RYSTIC_EVAL_WORKER_TOKEN
set, an evaluation worker’s X-Rystic-Worker-Token reaches only
/_rystic/eval/claim, /_rystic/eval/complete and /_rystic/eval/snapshot.
Browser pages and the simulator’s own routes. The browser pages, listed
under Browser pages, serve HTML. Routes a simulator adds under
/_ beside this control plane are listed in its API explorer,
/_rystic/swagger.
Request bodies are JSON. Errors are {"message": "..."} unless noted.
This page describes control-plane contract 27.
Routes
| Route | Methods | Feature | Summary |
|---|---|---|---|
/_feed | GET | every plan | Messages the inject schedule has fired |
/_rystic/actions | GET | state_view | Page of the action log |
/_rystic/activity | GET | same machine only | Activity counters |
/_rystic/calls | GET | state_view | Every vendor API request since reset |
/_rystic/clock | POST | state_set | Set or advance the simulator clock |
/_rystic/config | GET | state_view | The configuration a run used |
/_rystic/credentials | POST | state_set | Bind presented credentials to actors |
/_rystic/docs | GET | every plan | This build’s docs, by section |
/_rystic/docs/changelog | GET | every plan | Release notes up to this build |
/_rystic/docs/features/{feature} | GET | every plan | One feature’s page |
/_rystic/docs/{section} | GET | every plan | One section of the docs |
/_rystic/duplicate-delivery | GET, POST | faults | Duplicate delivery state |
/_rystic/egress | GET | state_view | Webhooks and WebSocket frames the simulator produced |
/_rystic/egress-retain | POST | state_set | Bound the egress and call logs |
/_rystic/eval | GET | state_view | Evaluation status |
/_rystic/eval/advance | POST | state_set | Publish ready evaluation results |
/_rystic/eval/calibrations | GET, POST | state_set | A calibration artifact |
/_rystic/eval/claim | POST | state_set | Claim dispatched evaluation work |
/_rystic/eval/complete | POST | state_set | Complete a dispatch |
/_rystic/eval/episode | GET, POST | state_set | The running episode’s configuration |
/_rystic/eval/evaluators | GET, POST | state_set | Registered evaluator manifests |
/_rystic/eval/evidence | GET | state_view | The task’s evidence |
/_rystic/eval/ledger | GET | state_view | Evaluation ledger |
/_rystic/eval/policy | GET, POST | state_set | The evaluation policy |
/_rystic/eval/reference | GET, POST | state_set | Harness-owned reference entries |
/_rystic/eval/results | GET | state_view | Evaluation requests and their current results |
/_rystic/eval/snapshot | GET | state_view | A snapshot archive for an evaluation worker |
/_rystic/events | GET | state_view | Server-Sent Events stream of activity |
/_rystic/files/{path} | GET, HEAD | a signed URL | Download a stored file by signed URL |
/_rystic/inject | GET, POST | state_set | The event schedule and what fired |
/_rystic/inspect | GET | state_view | The entire state |
/_rystic/network | GET, POST | faults | Network shaping state |
/_rystic/page-cap | GET, POST | faults | Page caps |
/_rystic/registers | GET | state_view | Every seedable register |
/_rystic/replies | GET, POST, DELETE | state_set | The scripted replies left and the ones taken |
/_rystic/reset | POST | every plan | Reset state |
/_rystic/revoke | GET, POST | faults | Revoked credentials |
/_rystic/routes | GET | every plan | Vendor API routes this build serves |
/_rystic/scenario | POST | scenarios | Reset, then load a named scenario |
/_rystic/scenarios | GET | scenarios | Named scenarios built into the simulator |
/_rystic/seed | POST | state_set | Merge records into state, or land a world document |
/_rystic/seed-rng | POST | state_set | Pin the random stream |
/_rystic/settle | GET | state_view | Wait until the app under test is done reacting |
/_rystic/version | GET | every plan | Identity and health |
/_rystic/webhooks | GET, POST, DELETE | state_set | The operator-set webhook URL |
/_rystic/ws | GET | state_view | Open WebSocket connections and their subscriptions |
/_rystic/ws-drop | POST | faults | Cut WebSocket connections |
/_feed
Methods: GET. Feature: every plan.
GET Messages the inject schedule has fired
Served only while an inject schedule is loaded (POST /_rystic/inject or
boot --inject); otherwise the path belongs to the vendor API.
Parameters
since(query, integer, at least 0): Return the messages after this sequence number. Default0.
Responses
200The fired messages, oldest first. Fields:events(array of object):body(any): The message as scheduled.channel(string)seq(integer)time(string, date-time)
400The body or query is malformed; the message names the expected shape.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
/_rystic/actions
Methods: GET. Feature: state_view.
GET Page of the action log
This process’s action-log lines with seq above since, as NDJSON. Lines
never carry a credential. Needs RYSTIC_ACTION_LOG set.
Parameters
since(query, integer, at least 0, default0)limit(query, integer, at least 1, at most 10000, default1000)
Responses
200NDJSON lines. (application/x-ndjson) String.- header
X-Rystic-More(boolean): Whether another line follows. - header
X-Rystic-Next-Since(integer): The lastseqserved; pass it assincefor the next page.
- header
400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404The action log is off.Error:message(string, required).500Error.Error:message(string, required).
/_rystic/activity
Methods: GET. Feature: same machine only.
GET Activity counters
Counters for the CLI on the same machine. Never gated by plan; any non-loopback caller gets 403.
Responses
200Counters.ActivityCounters:boot(string),requests(integer),subscriptions_closed(integer),subscriptions_open(integer),subscriptions_opened(integer),tape_starts(integer),tape_stops(integer),tapes_playing(integer),webhooks(integer).403Not a loopback caller.Error:message(string, required).
/_rystic/calls
Methods: GET. Feature: state_view.
GET Every vendor API request since reset
Responses
200Requests, oldest first.calls(array ofCall, required),total(integer, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/clock
Methods: POST. Feature: state_set.
POST Set or advance the simulator clock
At least one field is required. time applies before advance_seconds.
Request body (application/json, required)
Object, at least 1 field:
advance_seconds(integer): Seconds to move the clock.time(string, date-time): RFC 3339 time to set the clock to.wall_offset_seconds(integer): Offset applied to wall-clock reads;0removes it.
Responses
204Clock moved.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/config
Methods: GET. Feature: state_view.
GET The configuration a run used
Responses
200Configuration.Config:action_log(boolean, required)admin_token(boolean, required): Whether an admin token is required.commit(string, required)contract(integer, required)edition(string, required)initial_clock(string, date-time, required)inject(one of null, or an object, required):- null
- an object:
events(integer)sha256(string)
model_hash(string, required)product(string, required)replay(one of null, map of string to any, required)rng_seed(integer, required)scenario(string or null, required)seed(object, required): Seeds applied since reset and their chained digest.applied(integer)sha256(string)
version(string, required)
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/credentials
Methods: POST. Feature: state_set.
POST Bind presented credentials to actors
Replaces which actor each presented credential acts as, or with keep adds only the credentials no binding holds yet. Write-only. reset clears the bindings.
Request body (application/json, required)
bindings(map of string to string, required): Presented credential to actor label.keep(boolean): Keep every current binding, its actor and grants, and add only the credentials not bound yet.
Responses
204Bound.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/docs
Methods: GET. Feature: every plan.
GET This build’s docs, by section
The index of the docs built into this simulator, always JSON: each
section’s id, title and first_sentence, and each feature’s id,
title and summary. A build with no docs answers 404.
Responses
200The docs index.DocsIndex:features(array of object, required):id(string, required)summary(string, required)title(string, required)
product(string, required)sections(array of object, required):first_sentence(string, required)id(string, required)title(string, required)
version(string, required)
401An admin token is required and was not presented. (text/plain) String.404Not found, or not served by this simulator.Error:message(string, required).500Error.Error:message(string, required).
/_rystic/docs/changelog
Methods: GET. Feature: every plan.
GET Release notes up to this build
Every release up to this build, newest first, as Markdown; with
Accept: application/json, the list of entries.
Responses
200The Markdown text, or underAccept: application/jsonthe matching entry of the docs.401An admin token is required and was not presented. (text/plain) String.404Not found, or not served by this simulator.Error:message(string, required).500Error.Error:message(string, required).
/_rystic/docs/features/{feature}
Methods: GET. Feature: every plan.
GET One feature’s page
The feature’s page as Markdown; with Accept: application/json, its
entry in the docs. /_rystic/docs/features alone is the Features
section.
Parameters
feature(path, string, required)
Responses
200The Markdown text, or underAccept: application/jsonthe matching entry of the docs.401An admin token is required and was not presented. (text/plain) String.404No such id;idslists every id under /_rystic/docs/ that answers.ids(array of string, required),message(string, required).500Error.Error:message(string, required).
/_rystic/docs/{section}
Methods: GET. Feature: every plan.
GET One section of the docs
The section’s text as Markdown; with Accept: application/json, the
section’s entry in the docs. An unknown id answers 404 with ids,
every id that exists.
Parameters
section(path, string, required)
Responses
200The Markdown text, or underAccept: application/jsonthe matching entry of the docs.401An admin token is required and was not presented. (text/plain) String.404No such id;idslists every id under /_rystic/docs/ that answers.ids(array of string, required),message(string, required).500Error.Error:message(string, required).
/_rystic/duplicate-delivery
Methods: GET, POST. Feature: faults.
GET Duplicate delivery state
Responses
200The copies armed and how many have been queued since the last reset.copies(integer, required),duplicated(integer, int64, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
POST Deliver each event more than once
Each event’s first delivery attempt goes out copies more times, byte for byte with the same id and signature, each marked duplicate in GET /_rystic/egress’s delivery results and never retried. 0 disarms it, as a reset does.
Request body (application/json, required)
copies(integer, at least 0, at most 10, required)
Responses
204Armed.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/egress
Methods: GET. Feature: state_view.
GET Webhooks and WebSocket frames the simulator produced
Parameters
tail(query, integer, at least 0): Return only the last N events.raw(query, string:"1"):1keeps the service’s own hosts in URLs instead of rewriting them to the caller’s origin.
Responses
200The egress log, oldest first. Fields:dropped(integer, required): Events dropped by theegress-retainbound.events(array ofEgressEvent, required)total(integer, required)delivery(map of string to any): Delivery state, present when webhook delivery is on.
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/egress-retain
Methods: POST. Feature: state_set.
POST Bound the egress and call logs
0 is unbounded. Entries already dropped stay dropped.
Request body (application/json, required)
retain(integer, at least 0, required)
Responses
204Set.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/eval
Methods: GET. Feature: state_view.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET Evaluation status
Responses
200Episode status and counts.EvalStatus:dispatched(integer),dropped_late(integer),evaluators(array of string),generation(integer),policy_ref(string),published(integer),ready(integer),reference_digest(string),running(integer),spent_cost_units(integer),waiting(integer).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).409The evaluation reference could not load.Error:message(string, required).
/_rystic/eval/advance
Methods: POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
POST Publish ready evaluation results
settled (the default) waits for every dispatched request, lands answers in
request order and publishes; at the timeout it ends what is still out and
answers expired: true. step publishes what is ready now.
Request body (application/json, required)
timeout_ms(integer): Wait bound; default 30000, capped at 600000.until(string:"settled","step", default"settled")
Responses
200Advanced.episode_generation(integer, required),expired(boolean, required),outstanding(integer, required),published(integer, required),settled(boolean, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The episode changed (for example a reset) during the advance.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/calibrations
Methods: GET, POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET A calibration artifact
Parameters
id(query, string)
Responses
200The calibration artifact.OpenObject, map of string to any.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404No evaluation engine, or no such calibration.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).
POST Calibrate an evaluator version on held-out reference examples
Request body (application/json, required)
evaluator_id(string, required)evaluator_version(string, required)holdout_fraction(number)max_examples(integer)seed(string)
Responses
200Started.id(string, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/claim
Methods: POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
POST Claim dispatched evaluation work
Request body (application/json, required)
max(integer): Most dispatches to claim;1when absent or not positive.worker(string)
Responses
200Claimed dispatches. Fields:dispatches(array of object, required):dispatch(OpenObject, required)snapshot_url(string, required): Relative/_rystic/eval/snapshot?ref=...URL.
400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/complete
Methods: POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
POST Complete a dispatch
An answer after its deadline is accepted and lands as a timeout.
Request body (application/json, required)
dispatch_id(string, required)episode_generation(integer, required)outcome(EvalOutcome, required)request_id(string, required)
Responses
204Accepted.400Malformed, for another dispatch, or the request already ended.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409An answer from an earlier episode, or the reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/episode
Methods: GET, POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET The running episode’s configuration
Responses
200Episode configuration.OpenObject, map of string to any.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404No evaluation engine, or no episode running.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).
POST Freeze an episode
Takes the policy, permitted evaluator versions, pinned calibrations, an
optional reference_digest, task.scope and the task’s requirements.
Request body (application/json, required)
OpenObject, map of string to any.
Responses
200Started.episode_generation(integer, required),policy_ref(string, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/evaluators
Methods: GET, POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET Registered evaluator manifests
Responses
200Manifests.evaluators(array ofOpenObject, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).
POST Register an evaluator manifest
Versions are immutable.
Request body (application/json, required)
OpenObject, map of string to any.
Responses
204Registered.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/evidence
Methods: GET. Feature: state_view.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET The task’s evidence
One item per requirement, satisfied, failed or unevaluated, naming the current submission and snapshot.
Responses
200Evidence.OpenObject, map of string to any.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).409The evaluation reference could not load.Error:message(string, required).
/_rystic/eval/ledger
Methods: GET. Feature: state_view.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET Evaluation ledger
Requests, submissions (including empty ones) and published results, as NDJSON.
Responses
200NDJSON lines. (application/x-ndjson) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).409The evaluation reference could not load.Error:message(string, required).
/_rystic/eval/policy
Methods: GET, POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET The evaluation policy
Responses
200Policy and its reference.policy(OpenObject, required),policy_ref(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).
POST Set the evaluation policy
Setting a policy begins an episode.
Request body (application/json, required)
OpenObject, map of string to any.
Responses
200Set.generation(integer, required),policy_ref(string, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).413Request body too large.Error:message(string, required).
/_rystic/eval/reference
Methods: GET, POST. Feature: state_set.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET Harness-owned reference entries
Responses
200Entries.entries(array ofReferenceEntry, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The evaluation reference could not load.Error:message(string, required).
POST Replace the reference entries
Load before starting an episode; the episode freezes this digest and refuses a different set.
Request body (application/json, required)
entries(array ofReferenceEntry, required)
Responses
200Loaded.reference_digest(string, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404This simulator has no evaluation engine.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/eval/results
Methods: GET. Feature: state_view.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET Evaluation requests and their current results
Parameters
id(query, string): One request’s publication instead of the list.set(query, string): Filter the list to one set.
Responses
200{results: [...]}, or one{request, result}withid. One of an object, orEvalPublication:- an object:
results(array ofEvalPublication, required)
EvalPublication
- an object:
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404No evaluation engine, or no request with thatid.Error:message(string, required).409The evaluation reference could not load.Error:message(string, required).
/_rystic/eval/snapshot
Methods: GET. Feature: state_view.
Served only by a simulator with an evaluation engine; elsewhere 404.
GET A snapshot archive for an evaluation worker
A tar archive of the snapshot a dispatch names. At most two are served at once.
Parameters
ref(query, string, required)
Responses
200The snapshot. (application/x-tar) String,application/x-tarcontent.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404No evaluation engine, or no such snapshot.Error:message(string, required).500Error.Error:message(string, required).507The archive is larger than the simulator serves (RYSTIC_EVAL_SNAPSHOT_MAX, default 256 MiB).Error:message(string, required).
/_rystic/events
Methods: GET. Feature: state_view.
GET Server-Sent Events stream of activity
A long-lived stream of requests, control calls and effects. Each event’s
id: is its seq; resume with Last-Event-ID or ?cursor=.
Parameters
cursor(query, integer): Send only events with a greaterseq.Last-Event-IDwins when both are set.Last-Event-ID(header, string)
Responses
200An event stream; eachdata:line is anActivityEvent. (text/event-stream) String, contentActivityEvent.id: <seq>anddata: <ActivityEvent JSON>per event.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/files/{path}
Methods: GET, HEAD. Feature: a signed URL.
GET Download a stored file by signed URL
Use the URL exactly as the simulator returned it. No admin token needed.
Parameters
path(path, string, required): Any path; the query string identifies the file.resource(query, string, required)key(query, string, required)owner(query, string)expires(query, integer, required): Expiry in Unix milliseconds, on the simulator clock.signature(query, string, required)
Responses
200The file, under its stored content type. (*/*) String.403Signature mismatch or expired URL.Error:message(string, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
HEAD File headers by signed URL
Parameters
path(path, string, required): Any path; the query string identifies the file.resource(query, string, required)key(query, string, required)owner(query, string)expires(query, integer, required): Expiry in Unix milliseconds, on the simulator clock.signature(query, string, required)
Responses
200Headers only.403Signature mismatch or expired URL.404File not found.405Method not allowed.
/_rystic/inject
Methods: GET, POST. Feature: state_set.
GET The event schedule and what fired
Responses
200The schedule.InjectView:events(array of object, required):at(string, required)due(string, date-time, required)id(string, required)as(string)bind(map of string to string)bound(map of string to any)call(map of string to any)error(string)fired_at(string)message(map of string to any)seed(RegisterMap)status(integer)
fired(integer, required)pending(integer, required)
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
POST Replace the event schedule
Replaces the schedule. at resolves once against the clock when the
schedule loads, so a reset replays the same instants; an event already due
fires at once. The whole schedule is refused for an unknown route or an as
the scenario cannot act as. Unknown keys are refused.
Request body (application/json, required)
events(array ofInjectEvent, required)
Responses
204Loaded.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/inspect
Methods: GET. Feature: state_view.
GET The entire state
Every register as {"<register>": {"<key>": {record}}}. The registers and
record fields differ per simulator (see GET /_rystic/registers).
X-Rystic-Activity-Seq carries the /_rystic/events cursor at the read.
Parameters
view(query, string): A named view instead of the dump:identities;sequences, every id counter’s last value as{"sequences": {"<scope>": N}}, which a world document’ssequencestakes back; or a view the simulator declares. An unknown name answers404listing the ones it serves.etag(query, string:"1"):1adds anETagheader to pass asIf-MatchonPOST /_rystic/seed.
Responses
200The state dump, or the named view. Its shape is per simulator. Map of string to any.- header
ETag(string): Present with?etag=1. - header
X-Rystic-Activity-Seq(integer)
- header
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).500Error.Error:message(string, required).
/_rystic/network
Methods: GET, POST. Feature: faults.
GET Network shaping state
Responses
200Shaping state. Fields:enabled(boolean, required)profile(one ofNetworkProfile, null, required)source(string, required): Where the profile came from (nonewhen none is installed).
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
POST Install a network profile and/or switch shaping
Shapes vendor API requests only, never the control plane. {"enabled": true}
alone needs a profile already installed. A profile set here outlasts reset.
Request body (application/json, required)
NetworkInput, object, at least 1 field:
enabled(boolean)profile(NetworkProfile)
Responses
204Applied.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/page-cap
Methods: GET, POST. Feature: faults.
GET Page caps
Responses
200The capped routes and how many requests a cap has changed since the last reset. Fields:clamped(integer, int64, required)routes(map of string to object, required):max(integer)param(string)
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
POST Serve smaller pages than asked
On each "METHOD /template" route, the query param param is lowered to max when the caller asks more, and set to max when it asks nothing, so the caller must follow the route’s own continuation. {"routes": {}} clears every cap, as a reset does.
Request body (application/json, required)
routes(map of string to object, required):max(integer, at least 1, required)param(string, required)
Responses
204Armed.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/registers
Methods: GET. Feature: state_view.
GET Every seedable register
Responses
200The registers.contract(integer, required),model_hash(string, required),registers(array ofRegisterInfo, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404The build carries no seed manifest.Error:message(string, required).
/_rystic/replies
Methods: GET, POST, DELETE. Feature: state_set.
Served only by a simulator with an operation that answers a scripted reply, such as a model’s messages; elsewhere 404.
GET The scripted replies left and the ones taken
Responses
200The script.ScriptedReplies:left(array ofScriptedReply, required)operations(array of string, required): The routes that take replies.taken(array of object, required):operation(string, required)reply(integer, required): The reply’s index in the script.refused(string): Why the call was refused, when the reply called a tool the request did not declare.
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
POST Script what the model answers, turn by turn
Replaces the script. Each call to an operation that takes replies answers the
next one, in the service’s own shape, and the operation’s default answer once
none is left. A reply calling a tool the request does not declare is refused
and reported under taken. A reset starts the script over; a world document
or scenario can carry replies too.
Request body (application/json, required)
replies(array ofScriptedReply, required)
Responses
204Set.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
DELETE Empty the script
Responses
204Emptied.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
/_rystic/reset
Methods: POST. Feature: every plan.
POST Reset state
Clears state and logs, restarts id sequences, returns the clock to
initial_clock, reseeds the RNG and reseeds the simulator’s built-in records.
It does not re-apply a boot scenario. A scenario’s credential bindings, events and
network profile leave with it; a schedule loaded through /_rystic/inject
stays and replays. Ends any evaluation episode.
Responses
204Reset.405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
/_rystic/revoke
Methods: GET, POST. Feature: faults.
GET Revoked credentials
Each revoked credential by its SHA-256, never its value, with how many requests it has been refused and the call seq and simulator time of the first.
Responses
200The revocations armed since the last reset. Fields:revoked(array of object, required):after_calls(integer)credential_sha256(string)from_seq(integer, int64)refused(integer)revoked(boolean)revoked_at(string)
403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
POST Revoke credentials mid-run
Each listed credential is refused before any handler once after_calls more requests have presented it, with refusal when given, else the simulator’s own refusal of an unrecognized or dead credential; a simulator declaring none needs refusal. An empty list clears every revocation, as a reset does.
Request body (application/json, required)
credentials(array of string, required)after_calls(integer, at least 0)refusal(object):status(integer, required)body(any)headers(map of string to string)
Responses
204Armed.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/routes
Methods: GET. Feature: every plan.
GET Vendor API routes this build serves
Every vendor API route, sorted by template then method. The control plane is not in it.
Responses
200Route list.routes(array ofRouteInfo, required).401An admin token is required and was not presented. (text/plain) String.
/_rystic/scenario
Methods: POST. Feature: scenarios.
POST Reset, then load a named scenario
Request body (application/json, required)
name(string, required)params(map of string to one of string, number): A generated scenario’s parameters, each a string or a number. The name may carry them too, asname?size=large.
Responses
204Loaded.400A malformed body, or parameters the scenario does not take.Error:message(string, required).403The plan lacks thescenariosfeature, or one the scenario needs (such asfaultsfor its network profile).Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Unknown scenario; the message lists the ones that exist.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/scenarios
Methods: GET. Feature: scenarios.
GET Named scenarios built into the simulator
Responses
200Scenarios.scenarios(array ofScenarioInfo, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/seed
Methods: POST. Feature: state_set.
POST Merge records into state, or land a world document
Takes either a register map, merged into state, or a world document marked
by its top-level seed object, which lands whole or not at all: the clock
and RNG, then the seed and its id counters, the network profile, the
credential bindings and the events. Either way, a uuid or event id the
simulator minted, or an integer record id, that the seed holds is not minted
again; a world document’s
sequences carries the counters behind every other id. A world document’s other keys are ignored, except tape and loop,
which answer 400. Its network needs the faults feature.
Parameters
If-Match(header, string): AnETagfromGET /_rystic/inspect?etag=1;412if state changed since.
Request body (application/json, required)
One of RegisterMap, WorldDocument.
Responses
204Landed.400Invalid, incomplete or misplaced records, or a malformed body.SeedRejection:message(string, required),errors(array ofSeedError).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
412State changed since theETagwas read.Error:message(string, required).413Request body too large.Error:message(string, required).500Error.Error:message(string, required).
/_rystic/seed-rng
Methods: POST. Feature: state_set.
POST Pin the random stream
Ids and draws repeat run to run under the same seed.
Request body (application/json, required)
seed(integer, int64, required)
Responses
204Seeded.400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_rystic/settle
Methods: GET. Feature: state_view.
GET Wait until the app under test is done reacting
A long-poll a driver calls after acting. It answers settled: true once
no event or interaction delivery is open (a Socket Mode envelope awaiting
its ack, a request-URL delivery queued or in flight, a retry due by now)
and no watched call has landed for quiet_ms of wall time, counted from
the later of the last watched call and the last ack or reset. A watched
call is any vendor API call except one carrying X-Rystic-Surface: client
or X-Rystic-Source: ui, or a timeline event’s; principal narrows them.
Quiet is wall time because the app runs in real time while the
simulator’s clock may be pinned or jumped. At timeout_ms it answers
settled: false with pending, what is still open. A retry due later
never holds it and is listed in scheduled. last_call_seq is the last
seq in GET /_rystic/calls: settle before acting and after, and the
calls between the two seqs are the app’s turn.
Parameters
quiet_ms(query, integer, at least 0, at most 60000, default500): Wall milliseconds with no watched call, 0 to 60000.timeout_ms(query, integer, at least 0, at most 60000, default30000): Wall milliseconds to wait before answering unsettled, 0 to 60000.principal(query, string): Watch only calls whoseactororprincipalthis names; repeat it or separate names with commas.
Responses
200Settled, or the timeout with what is still open.Settle:idle_ms(integer, required): Wall milliseconds since the quiet window opened.last_call_seq(integer, required): The lastseqinGET /_rystic/calls, 0 since a reset with no call.settled(boolean, required)pending(array ofSettleEntry): Only when unsettled. What is still open, and the call, ack or reset the quiet window counts from while it is shorter thanquiet_ms.scheduled(array ofSettleEntry): Retries due later, which settle never waits on.
400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/version
Methods: GET. Feature: every plan.
GET Identity and health
Identity and health. Open even when an admin token is set, so it serves as a
readiness check. A stopping simulator keeps answering here with stopping.
Responses
200Simulator identity.Version:clock(string, date-time, required)commit(string, required)contract(integer, required)edition(string, required):meteredon a simulator that runs on a lease, empty on one that runs on a license file.initial_clock(string, date-time, required)model_hash(string, required)network(object, required):enabled(boolean)endpoints(integer)profile_digest(string)
product(string, required)version(string, required)api_base_paths(array of string)auth_scheme(string)blob_cap_bytes(integer)body_accepted(array of string)body_ct_selects(boolean)compat_auth(boolean)compat_body_encoding(boolean)credential_required(integer)egress_retain(integer)env(map of string to any)explorer(array of string): Explorer tools served, each at/_rystic/<tool>.lease(object): Set only on a simulator that runs on a lease.entitlements(map of string to any)expires_at(any)notice(any)offline(boolean)offline_until(integer)session_id(string)
license_expires(any): Set only on a simulator that runs on a license file.license_features(array of string)licensee(string): Set only on a simulator that runs on a license file.preserve_service_urls(boolean)service_hosts(array of string)stopping(any): Present while the simulator is stopping, with the reason.watermark(string)
/_rystic/webhooks
Methods: GET, POST, DELETE. Feature: state_set.
Served only by a simulator whose service has a webhook set in its console; elsewhere 404.
GET The operator-set webhook URL
Never returns the secret.
Responses
200Webhook state.OperatorWebhook:active(boolean, required),delivery(string:"on","off", required),notifications(integer, required),pending(integer, required),url(string or null, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
POST Set the operator webhook URL
Makes the vendor’s challenge handshake with the URL first, where the vendor
has one, and sets it only when it passes. A reset keeps the URL.
Request body (application/json, required)
url(string, uri, required): Absolute http(s) URL without userinfo or fragment.secret(string): Signing secret; required unless the vendor’s signature takes none.settings(map of string to string): Every setting the simulator declares, and no other.
Responses
200Set.OperatorWebhook:active(boolean, required),delivery(string:"on","off", required),notifications(integer, required),pending(integer, required),url(string or null, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409Webhook delivery is off; boot withRYSTIC_WEBHOOK_DELIVERY=on.Error:message(string, required).413Request body too large.Error:message(string, required).422The URL failed the handshake; the previous URL stays.Error:message(string, required).
DELETE Clear the operator webhook URL
Responses
204Cleared.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
/_rystic/ws
Methods: GET. Feature: state_view.
GET Open WebSocket connections and their subscriptions
Responses
200Connections.connections(array of map of string to any, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_rystic/ws-drop
Methods: POST. Feature: faults.
POST Cut WebSocket connections
Set all, or name connections or tickers. graceful sends a 1001 close frame first.
Request body (application/json, required)
all(boolean)connections(array of integer, int64)graceful(boolean)tickers(array of string)
Responses
200The connection ids cut ([]when none matched).dropped(array of integer, int64, required).400The body or query is malformed; the message names the expected shape.Error:message(string, required).403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
Tape player routes
Served only by a simulator with a tape player; elsewhere 404.
| Route | Methods | Feature | Summary |
|---|---|---|---|
/_replay/feed | POST | replay | Push live frames |
/_replay/markets | GET | replay | Recorded markets this simulator can replay |
/_replay/pause | POST | replay | Pause playback |
/_replay/play | POST | replay | Resume, change speed or play to a stop |
/_replay/prepare | POST | replay | Load a recording ahead of start |
/_replay/seek | POST | replay | Jump to a recorded position |
/_replay/start | POST | replay | Start a replay or a live session |
/_replay/status | GET | replay | The session’s play head |
/_replay/stream | GET | replay | A recording as NDJSON |
/_replay/watch | GET, POST | replay | The live session’s market lists |
/_replay/feed
Methods: POST. Feature: replay.
POST Push live frames
Applies one batch of live frames, in order, each at its own exchange
timestamp. The body is NDJSON in the recording’s own line format;
blank lines are skipped. Frames for markets outside the session’s
list are dropped as unknown_market; a paused session drops the
whole batch as paused. A failed frame pauses the session with its
error.
Request body (application/x-ndjson, required)
String.
Responses
200Batch applied. Fields:accepted(integer, required): Frames applied.dropped(integer, required): Lines dropped, the sum ofdrops.drops(map of string to integer, required): Lines dropped, by reason.
400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409No live session.Error:message(string, required).413Request body too large.Error:message(string, required).500Error.Error:message(string, required).
/_replay/markets
Methods: GET. Feature: replay.
GET Recorded markets this simulator can replay
The recorded markets the simulator holds. spans covers only the
recordings this process has already loaded; it is absent when none
has. During a live session, live lists the markets the feed has
carried, and the listing answers even with no recordings at all.
Any method is accepted; GET is documented.
Responses
200Recorded markets. Fields:markets(array of string, required)live(array of string): Present only during a live session.spans(array ofReplayGroupMember)
401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).500Error.Error:message(string, required).
/_replay/pause
Methods: POST. Feature: replay.
POST Pause playback
Stops the play head where it stands. A live session drops the frames fed while paused.
Responses
204Paused.401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
413Request body too large.Error:message(string, required).
/_replay/play
Methods: POST. Feature: replay.
POST Resume, change speed or play to a stop
Resumes a paused session. A finished recording plays again from the
top. An empty body is a plain resume. Give at most one stop:
until_ms, advance_ms or until. A live session takes no stop and
speed 1 only.
Request body (application/json)
advance_ms(integer, int64, above 0): Pause this many recorded milliseconds past the play head.speed(number, above 0): New playback speed. Absent keeps the current one.until(string:"next_event"): Pause at the next pending scheduled event (statusnext_event_ms).until_ms(integer, int64): Pause after every event through this absolute recorded time (statusstart_ms+position_ms). Not behind the play head.
Responses
204Playing.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409Nothing is loaded; a stop was asked of a live session;until_msis behind the play head; a step was asked of a finished recording; oruntil: next_eventhas no pending event.Error:message(string, required).413Request body too large.Error:message(string, required).
/_replay/prepare
Methods: POST. Feature: replay.
POST Load a recording ahead of start
Loads the named market, or a synchronized group, and holds it for the
next start with the same selector, so that start does not wait for
the load. A later prepare replaces the held one.
Request body (application/json, required)
copies(integer, at least 0)market(string)markets(array ofReplayMember)
Responses
204Loaded.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The group’s recordings do not overlap in time.Error:message(string, required).413Request body too large.Error:message(string, required).501Anasrename was asked of a simulator that cannot play a recording under another ticker.Error:message(string, required).
/_replay/seek
Methods: POST. Feature: replay.
POST Jump to a recorded position
Sets the simulator’s state to what it was after the event at
position and moves the play head there, playing or paused. A
position past either end is clamped to it. Checkpoints before the new
head are dropped. A sequence or a live session cannot seek.
Request body (application/json, required)
position(integer): Event index, 0-based (statusposition, belowtotal).
Responses
204Moved.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409Nothing is loaded, the session is live or a sequence, or the session changed during the seek.Error:message(string, required).413Request body too large.Error:message(string, required).500Error.Error:message(string, required).
/_replay/start
Methods: POST. Feature: replay.
POST Start a replay or a live session
Ends any running session and plays from the top. Name one market, a
markets group (merged and trimmed to the window all of them cover),
or a sequence of games played back to back on one session clock.
{"mode": "live"} instead opens a live session fed through
/_replay/feed; ["*"] as its markets admits any market the feed
carries.
Request body (application/json, required)
All of ReplaySelector, plus:
checkpoints(array of integer, int64): Pause after every event through each of these times, in absolute recorded epoch milliseconds (the axis ofstatusstart_ms). Not withlive.gap_ms(integer, int64): Session milliseconds between one game’s end and the next game’s start. Sequence only.mode(string):liveopens a live session; any other value plays a recording.rebase(boolean): Shift the simulator clock so the first recorded event lands at the current wall time.sequence(array of one ofReplayMember, array ofReplayMember): Games in play order, each a market, a{tape, as}entry or a list of them played as one group. Names nomarket,marketsorcopiesbeside it, and nolivemode.session_start_ms(integer, int64): Epoch milliseconds the first game starts at on the session clock. Sequence only.speed(number): Playback speed;1is recorded time. Absent or not above 0 plays at1.
Responses
204Started.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409The group’s recordings do not overlap in time.Error:message(string, required).413Request body too large.Error:message(string, required).501Anasrename was asked of a simulator that cannot play a recording under another ticker, orlivewas asked of one that cannot be fed.Error:message(string, required).
/_replay/status
Methods: GET. Feature: replay.
GET The session’s play head
Where the session stands. position_ms and duration_ms are relative
to start_ms; start_ms and end_ms are absolute recorded time,
before any rebase. Any method is accepted; GET is documented.
Responses
200Session status.ReplayStatus:done(boolean, required)duration_ms(integer, int64, required)playing(boolean, required)position(integer, required)position_ms(integer, int64, required)rebase_offset_ms(integer, int64, required): The clock shiftrebasechose at start;0without it.total(integer, required)at_checkpoint_ms(integer, int64): The stop the session is paused at.checkpoints(array of integer, int64): Pending stops, absolute recorded milliseconds.current_game(integer): The 1-based game the play head last entered.dropped(integer, int64): Live frames dropped.end_ms(integer, int64)error(string)frames(integer, int64): Live frames applied.lag_ms(integer, int64): Wall time now minuslast_ms.last_ms(integer, int64): Exchange timestamp of the last live frame applied.market(string): The market, a comma-joined group, or*for a live session admitting any market.markets(array ofReplayGroupMember)mode(string:"apply","seed","live"):applydrives each event through the simulator’s own state and messages;seedreplaces state with each event’s snapshot;liveis fed through/_replay/feed.next_event_ms(integer, int64): Recorded time of the next pending scheduled event.sequence(array ofReplaySequenceGame)speed(number)start_ms(integer, int64)
401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).
/_replay/stream
Methods: GET. Feature: replay.
GET A recording as NDJSON
Every recorded event of market, one JSON object per line, from a
fresh load: the session and its play head are untouched. The fields
of each line are the simulator’s own. A body that ends early means the
recording failed partway. Any method is accepted; GET is documented.
Parameters
market(query, string, required)
Responses
200One event per line. (application/x-ndjson) Map of string to any.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).404Not found, or not served by this simulator.Error:message(string, required).501This simulator’s recordings do not stream.Error:message(string, required).
/_replay/watch
Methods: GET, POST. Feature: replay.
GET The live session’s market lists
Outside a live session every list is empty.
Responses
200Market lists. Fields:available(array of string, required): Every market the feeder can subscribe to.seen(array of string, required): The markets frames have arrived for.watch(array of string, required): The markets chosen to carry. Empty means all ofavailable.
401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
POST Set the live session’s market lists
Sets each list the body names and leaves the other as it is, so the
feeder (available) and a viewer (markets) never overwrite each
other. Lists are stored sorted.
Request body (application/json, required)
Object, at least 1 field:
available(array of string): Every market the feeder can subscribe to.markets(array of string): The markets to carry. Empty carries all ofavailable.
Responses
204Set.400The body or query is malformed; the message names the expected shape.Error:message(string, required).401An admin token is required and was not presented. (text/plain) String.403The plan does not include this route’s feature.Unentitled:feature(string:"state_view","state_set","scenarios","faults","ui","replay", required),message(string, required),upgrade_url(string, uri, required).405Method not allowed;Allowlists the methods the route takes.Error:message(string, required).- header
Allow(string)
- header
409No live session.Error:message(string, required).413Request body too large.Error:message(string, required).
Browser pages
/_rystic/ui
Feature: ui. Served as HTML.
The state UI, the simulator’s state in the browser.
/_rystic/swagger
Feature: ui. Served as HTML.
The API explorer, Swagger UI, for this control plane and, where the simulator has one, its own API’s OpenAPI document.
/_rystic/graphiql
Feature: ui. Served as HTML.
The GraphQL explorer, GraphiQL, for the simulator’s GraphQL API.
Served only by a simulator whose API has a GraphQL schema; elsewhere 404.
Schemas
The types the routes above name, by name.
ActivityCounters
boot(string)requests(integer)subscriptions_closed(integer)subscriptions_open(integer)subscriptions_opened(integer)tape_starts(integer)tape_stops(integer)tapes_playing(integer)webhooks(integer)
ActivityEvent
at(string, required)kind(string:"request","effect","control", required)seq(integer, required)data(map of string to any)
Call
One vendor API request. Further fields vary.
actor(string)at(string)duration_ms(number)method(string)path(string)principal(string)seq(integer)status(integer)surface(string:"client"): Present on a call that carriedX-Rystic-Surface: client, standing in for the vendor’s own client.template(string)- any other key (any)
Config
action_log(boolean, required)admin_token(boolean, required): Whether an admin token is required.commit(string, required)contract(integer, required)edition(string, required)initial_clock(string, date-time, required)inject(one of null, or an object, required):- null
- an object:
events(integer)sha256(string)
model_hash(string, required)product(string, required)replay(one of null, map of string to any, required)rng_seed(integer, required)scenario(string or null, required)seed(object, required): Seeds applied since reset and their chained digest.applied(integer)sha256(string)
version(string, required)
DelaySpec
Base, jitter and correlation, or a quantile table, which wins when present.
base_ms(number, at least 0)correlation(number, at least 0, below 1)jitter_ms(number, at least 0)quantiles_ms(map of string to number):p50(required),p90,p99.
DocsIndex
features(array of object, required):id(string, required)summary(string, required)title(string, required)
product(string, required)sections(array of object, required):first_sentence(string, required)id(string, required)title(string, required)
version(string, required)
EgressEvent
effect(string, required)seq(integer, required)type(string, required)owner(string)payload(map of string to any): Per simulator.
EndpointShape
in(DelaySpec)out(DelaySpec)reject(RejectSpec)
Error
message(string, required)
EvalOutcome
An evaluated outcome has a verdict; a skip or error has a reason.
evaluation(string, required)evidence(OpenObject)outputs(map of string to string)reason(string)verdict(string)
EvalPublication
request(OpenObject, required)result(OpenObject, required)
EvalStatus
dispatched(integer)dropped_late(integer)evaluators(array of string)generation(integer)policy_ref(string)published(integer)ready(integer)reference_digest(string)running(integer)spent_cost_units(integer)waiting(integer)
InjectEvent
Exactly one of message, seed or call.
at(string, required): RFC 3339 simulator time, or+<duration>from the clock at load.id(string, required): Unique within the schedule.as(string): Actor a vendor-route call acts as;memberwhen absent.bind(map of string to string): Name to JSON Pointer into the call’s answer, usable as${name}by later events.call(object): At most one ofbody,body_text,body_base64.method(string, required)path(string, required): A vendor route, or one of the simulator’s own/_routes outside/_rystic/and/_feed.body(any): JSON object, or array where the route reads one.body_base64(string,base64encoded)body_text(string)headers(map of string to string): May not setAuthorization,Content-Typeor other headers the simulator sets.
message(object): Lands onGET /_feed.channel(string, required)body(any)
seed(RegisterMap)
InjectView
events(array of object, required):at(string, required)due(string, date-time, required)id(string, required)as(string)bind(map of string to string)bound(map of string to any)call(map of string to any)error(string)fired_at(string)message(map of string to any)seed(RegisterMap)status(integer)
fired(integer, required)pending(integer, required)
NetworkInput
Set enabled, profile or both.
Object, at least 1 field:
enabled(boolean)profile(NetworkProfile)
NetworkProfile
endpoints(map of string toEndpointShape, required): KeyedMETHOD /templateexactly asGET /_rystic/routeslists them.id(string, required)default(EndpointShape)effects(map of string toDelaySpec)enabled_by_default(boolean)
OpenObject
Map of string to any.
OperatorWebhook
active(boolean, required)delivery(string:"on","off", required)notifications(integer, required)pending(integer, required)url(string or null, required)
ReferenceEntry
job(OpenObject, required)snapshot(OpenObject, required)verdict(string, required)context(map of string to string)evidence(OpenObject)outputs(map of string to string)qualifier(string)
RegisterInfo
id(string, required)keyed(boolean, required)kind(string, required)seed_via(string, required)fields(array of object):name(string, required)default(any)format(string)nullable(boolean)ref(string)required(boolean)type(string)
key_fields(array of string)namespace(map of string to any)owner_field(string)tombstones(boolean)
RegisterMap
{"<register>": {"<key>": {"<field>": value}}}. Registers and fields are per simulator; see GET /_rystic/registers. A partial record merges into the one at its key. GET /_rystic/inspect is in this shape and seeds back as it reads: a per-caller register’s key <owner>\u0000<key> lands in that owner’s records.
Map of string to map of string to map of string to any.
RejectSpec
Answers a fraction of requests, and the exact calls on_calls numbers, without running them. body and body_text are exclusive.
status(integer, required)body(any): JSON body, written verbatim.body_text(string)content_type(string)headers(map of string to string)on_calls(array of integer, at least 1, unique items): 1-based call numbers to reject, counted per endpoint over every call since reset or profile install, shaped or not.rate(number, at least 0, at most 1): Per-request rejection probability; absent is 0.
ReplayGroupMember
end_ms(integer, int64, required)market(string, required)start_ms(integer, int64, required)total(integer, required): Events kept after the group’s overlap trim.game(integer): The member’s 1-based game in a sequence.tape(string): The recording played, when it differs frommarket.
ReplayMember
A recording name, or {tape, as} to play the recording under another ticker.
One of string, or an object:
- string
- an object:
tape(string, required)as(string)
ReplaySelector
market or markets, not both. Market names must be distinct after renames. copies above 1 plays each member that many times, copy k as <market>-<k>; tape replay only.
copies(integer, at least 0)market(string)markets(array ofReplayMember)
ReplaySequenceGame
One game of a sequence. Session time t maps to recorded time t - offset_ms.
end_ms(integer, int64, required)game(integer, required)markets(array of string, required)offset_ms(integer, int64, required)recorded_end_ms(integer, int64, required)recorded_start_ms(integer, int64, required)start_ms(integer, int64, required)
ReplayStatus
done(boolean, required)duration_ms(integer, int64, required)playing(boolean, required)position(integer, required)position_ms(integer, int64, required)rebase_offset_ms(integer, int64, required): The clock shiftrebasechose at start;0without it.total(integer, required)at_checkpoint_ms(integer, int64): The stop the session is paused at.checkpoints(array of integer, int64): Pending stops, absolute recorded milliseconds.current_game(integer): The 1-based game the play head last entered.dropped(integer, int64): Live frames dropped.end_ms(integer, int64)error(string)frames(integer, int64): Live frames applied.lag_ms(integer, int64): Wall time now minuslast_ms.last_ms(integer, int64): Exchange timestamp of the last live frame applied.market(string): The market, a comma-joined group, or*for a live session admitting any market.markets(array ofReplayGroupMember)mode(string:"apply","seed","live"):applydrives each event through the simulator’s own state and messages;seedreplaces state with each event’s snapshot;liveis fed through/_replay/feed.next_event_ms(integer, int64): Recorded time of the next pending scheduled event.sequence(array ofReplaySequenceGame)speed(number)start_ms(integer, int64)
RouteInfo
method(string, required)template(string, required)target(string): The route an alias row stands for.target_method(string)
ScenarioInfo
frames(integer, required)name(string, required)summary(string, required)loop(boolean)params(array of object): A generated scenario’s parameters, each with a default;clockandrng_seedcome last.kind(string:"enum","int","string","time", required)name(string, required)default(string)enum(array of string)max(integer)min(integer)summary(string)
ScriptedReplies
left(array ofScriptedReply, required)operations(array of string, required): The routes that take replies.taken(array of object, required):operation(string, required)reply(integer, required): The reply’s index in the script.refused(string): Why the call was refused, when the reply called a tool the request did not declare.
ScriptedReply
content(array of object, required):type(string:"text","tool_use", required)id(string): The call id to answer; absent, the simulator mints one.input(object): A tool_use block’s input.name(string): The tool a tool_use block calls; the request must declare it.text(string): A text block’s text.
stop_reason(string): Why the reply stopped; absent, the simulator answers the service’s own for the content.
SeedError
error(string, required)register(string, required)key(string)
SeedRejection
message(string, required)errors(array ofSeedError)
Settle
idle_ms(integer, required): Wall milliseconds since the quiet window opened.last_call_seq(integer, required): The lastseqinGET /_rystic/calls, 0 since a reset with no call.settled(boolean, required)pending(array ofSettleEntry): Only when unsettled. What is still open, and the call, ack or reset the quiet window counts from while it is shorter thanquiet_ms.scheduled(array ofSettleEntry): Retries due later, which settle never waits on.
SettleEntry
kind(string:"socket_mode_envelope","socket_mode_interaction","delivery","interaction","call","ack","reset","socket_mode_retry","delivery_retry", required)app(string): The app a Socket Mode envelope or interaction goes to; absent for the default app.attempt(integer): The attempt, absent for the first.call_seq(integer): Forcall, the watched call’sseqinGET /_rystic/calls.due_at(string, date-time): The wall instant a retry fires.due_clock(string, date-time): The simulator-clock instant a request-URL retry fires, if the clock reaches it first.endpoint(string): The registered endpoint a delivery goes to.event_seq(integer): The delivered event’sseq, asGET /_rystic/egressnames it.id(string): A Socket Mode envelope’senvelope_id.idle_ms(integer)state(string:"queued","in_flight","due")
Unentitled
Example: {"feature":"faults","message":"/_rystic/network needs the \"faults\" feature, which this plan does not include; upgrade at https://www.rystic.ai/pricing","upgrade_url":"https://www.rystic.ai/pricing"}.
feature(string:"state_view","state_set","scenarios","faults","ui","replay", required)message(string, required)upgrade_url(string, uri, required)
Version
clock(string, date-time, required)commit(string, required)contract(integer, required)edition(string, required):meteredon a simulator that runs on a lease, empty on one that runs on a license file.initial_clock(string, date-time, required)model_hash(string, required)network(object, required):enabled(boolean)endpoints(integer)profile_digest(string)
product(string, required)version(string, required)api_base_paths(array of string)auth_scheme(string)blob_cap_bytes(integer)body_accepted(array of string)body_ct_selects(boolean)compat_auth(boolean)compat_body_encoding(boolean)credential_required(integer)egress_retain(integer)env(map of string to any)explorer(array of string): Explorer tools served, each at/_rystic/<tool>.lease(object): Set only on a simulator that runs on a lease.entitlements(map of string to any)expires_at(any)notice(any)offline(boolean)offline_until(integer)session_id(string)
license_expires(any): Set only on a simulator that runs on a license file.license_features(array of string)licensee(string): Set only on a simulator that runs on a license file.preserve_service_urls(boolean)service_hosts(array of string)stopping(any): Present while the simulator is stopping, with the reason.watermark(string)
WorldDocument
A scenario file. Other keys are ignored, except tape and loop, which are refused.
seed(RegisterMap, required)clock(string, date-time)credentials(map of string to string): Presented credential to actor label.events(array ofInjectEvent): The scenario’s own schedule; offsets resolve againstclock.network(NetworkInput)rng_seed(integer, int64)sequences(map of string to integer, int64, at least 0): Id counter to the last value it reached, asGET /_rystic/inspect?view=sequencesserves them. Each counter rises to at least its value once the seed lands, so the next record created gets an id the seed does not hold.