<button class="accordion"><strong>Java Code Example</strong></button>
<div class="panel">
    <p><em><strong>Please Note:</strong> There are over 100 more detailed code examples in Java, JavaScript and the REST API below.</em></p>

    <pre class="prettyprint lang-java code"><code class="code">new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/login")
            .withBody("{username: 'foo', password: 'bar'}")
    )
    .respond(
        response()
            .withStatusCode(302)
            .withCookie(
                "sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
            )
            .withHeader(
                "Location", "https://www.mock-server.com"
            )
    );</code></pre>
</div>

<button class="accordion"><strong>JavaScript Code Example</strong></button>
<div class="panel">
    <p><em><strong>Please Note:</strong> There are over 100 more detailed code examples in Java, JavaScript and the REST API below.</em></p>

    <pre class="prettyprint lang-java code"><code class="code">var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/login",
        "body": {
            "username": "foo",
            "password": "bar"
        }
    },
    "httpResponse": {
        "statusCode": 302,
        "headers": {
            "Location": [
                "https://www.mock-server.com"
            ]
        },
        "cookies": {
            "sessionId": "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);</code></pre>
</div>

<p>To use the Java client add the <strong>org.mock-server:mockserver-client-java-no-dependencies:{{ site.mockserver_version }}</strong> dependency. The <code>-no-dependencies</code> artifact bundles all dependencies with relocated packages, so it declares zero transitive dependencies — this avoids classpath conflicts and CVE scanning noise from unused transitive dependencies.</p>

<p>For more details about the different dependency versions see the page on <a href="/where/maven_central.html#mockserver_client_java">Maven Central</a></p>

<p>for example in maven:</p>

<pre><code class="code xml">&lt;<span class="element">dependency</span>&gt;
	&lt;<span class="element">groupId</span>&gt;<span class="element_value">org.mock-server</span>&lt;/<span class="element">groupId</span>&gt;
	&lt;<span class="element">artifactId</span>&gt;<span class="element_value">mockserver-client-java-no-dependencies</span>&lt;/<span class="element">artifactId</span>&gt;
	&lt;<span class="element">version</span>&gt;<span class="element_value">{{ site.mockserver_version }}</span>&lt;/<span class="element">version</span>&gt;
&lt;/<span class="element">dependency</span>&gt;</code></pre>

<p>A <strong>request matcher expectation</strong> may contain:</p>
<ul>
    <li><strong><a href="#request_matchers">request matcher</a></strong> - used to match which requests this expectation should be applied to</li>
    <li><strong><a href="#actions">action</a></strong> - what action to take, actions include <strong>response</strong>, <strong>forward</strong>, <strong><a href="#forward_with_fallback_action">forward with fallback</a></strong>, <strong>callback</strong> and <strong>error</strong></li>
    <li><strong><a href="#button_match_request_by_path_exactly_twice">times</a></strong> <em>(optional)</em> - how many times the action should be taken</li>
    <li><strong><a href="#button_match_request_by_path_exactly_once_in_the_next_60_seconds">timeToLive</a></strong> <em>(optional)</em> - how long the expectation should stay active</li>
    <li><strong><a href="#button_match_request_by_priority">priority</a></strong> <em>(optional)</em> - matching is ordered by priority (highest first) then creation (earliest first)</li>
    <li><strong><a href="#button_match_request_update_by_id">id</a></strong> <em>(optional)</em> - used for updating an existing expectation (i.e. when the id matches)</li>
    <li><strong><a href="#stateful_scenarios">scenario</a></strong> <em>(optional)</em> - used for stateful multi-step API flows where the response changes based on prior interactions</li>
    <li><strong><a href="#sequential_responses">httpResponses</a></strong> <em>(optional)</em> - an array of responses to cycle through sequentially or randomly on each match</li>
</ul>

<p><strong><a href="/mock_server/using_openapi.html#generate_expectation_from_openapi">open api expectations</a></strong> are also supported using an <a target="_blank" href="https://swagger.io/docs/specification/basic-structure/" rel="noopener noreferrer"><strong>OpenAPI
    v3</strong></a> specifications to generate <strong>request matcher expectations</strong> for each operation, see the section on <strong><a href="/mock_server/using_openapi.html#generate_expectation_from_openapi">open api
    expectations</a></strong> for details.</p>

<a id="matching_order" class="anchor" href="#matching_order">&nbsp;</a>

<h4>Matching Order</h4>

<p>MockServer will match (or play) active expectations in the exact order they are added (if their priority is identical). For example, if an expectation <span class="annotation">A</span> is added with <span class="inline_code">Times.exactly(3)</span>
    then expectation <span class="annotation">B</span> is added with <span class="inline_code">Times.exactly(2)</span> with the same request matcher they will be applied in the following order <span class="annotation">A</span>, <span
            class="annotation">A</span>, <span class="annotation">A</span>, <span class="annotation">B</span>, <span class="annotation">B</span>. Priority can be used to alter the order that expectations are matched; matching is ordered by
    priority (highest first) then creation (earliest first).</p>
<p>Priority can be used to configure a <a href="#button_match_request_by_negative_priority">default expectation or response</a> by specifying a negative value for priority and a very lax request matcher; the lax request matcher ensures the
    default expectation is always matched, but the low priority ensure it is matched last after all other expectations.</p>

<a id="probabilistic_matching" class="anchor" href="#probabilistic_matching">&nbsp;</a>

<h4>Probabilistic Matching (Percentage)</h4>

<p>An expectation can be configured with a <strong>percentage</strong> (0-100) to enable probabilistic matching. When set, the expectation will only match the specified percentage of requests that structurally match the request matcher. This is useful for simulating intermittent failures, flaky services, or A/B testing scenarios.</p>
<p>For example, setting <span class="inline_code">percentage</span> to <span class="inline_code">50</span> means approximately half of matching requests will be handled by this expectation, while the other half will fall through to the next matching expectation or the default behavior.</p>
<p>If <span class="inline_code">percentage</span> is not set or set to <span class="inline_code">100</span>, the expectation matches all structurally matching requests (the default behavior). A value of <span class="inline_code">0</span> means the expectation never matches.</p>

<a id="stateful_scenarios" class="anchor" href="#stateful_scenarios">&nbsp;</a>

<h4>Stateful Scenarios</h4>

<p>Stateful scenarios let you model <strong>multi-step API conversations</strong> where MockServer's response changes based on prior interactions. Each scenario is an independent named state machine that starts in the <span class="inline_code">"Started"</span> state.</p>

<p>An expectation can participate in a scenario by specifying:</p>
<ul>
    <li><strong>scenarioName</strong> - the name of the scenario this expectation belongs to</li>
    <li><strong>scenarioState</strong> - the state the scenario must be in for this expectation to match</li>
    <li><strong>newScenarioState</strong> - the state the scenario transitions to after this expectation matches</li>
</ul>

<p>If <span class="inline_code">scenarioName</span> is not set, the expectation behaves normally without any scenario filtering. The default initial state for all scenarios is <span class="inline_code">"Started"</span>.</p>

<p><strong>Example - Login Flow:</strong> First call returns a token, subsequent calls using that token get a different response.</p>
<pre class="prettyprint lang-java code"><code class="code">// Step 1: Login returns a token (scenario starts in "Started" state)
{
    "httpRequest": {
        "method": "POST",
        "path": "/login"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"token\": \"abc123\"}"
    },
    "scenarioName": "LoginFlow",
    "scenarioState": "Started",
    "newScenarioState": "LoggedIn",
    "times": { "remainingTimes": 1 }
}

// Step 2: After login, GET /profile returns user data
{
    "httpRequest": {
        "method": "GET",
        "path": "/profile"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}"
    },
    "scenarioName": "LoginFlow",
    "scenarioState": "LoggedIn"
}

// Step 3: Before login, GET /profile returns 401
{
    "httpRequest": {
        "method": "GET",
        "path": "/profile"
    },
    "httpResponse": {
        "statusCode": 401,
        "body": "{\"error\": \"Not authenticated\"}"
    },
    "scenarioName": "LoginFlow",
    "scenarioState": "Started"
}</code></pre>

<p><strong>Example - Pagination:</strong> Successive calls to the same endpoint return different pages of data.</p>
<pre class="prettyprint lang-java code"><code class="code">// Page 1 (initial state)
{
    "httpRequest": {
        "method": "GET",
        "path": "/items"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"items\": [\"a\", \"b\"], \"page\": 1, \"hasMore\": true}"
    },
    "scenarioName": "Pagination",
    "scenarioState": "Started",
    "newScenarioState": "Page2",
    "times": { "remainingTimes": 1 }
}

// Page 2
{
    "httpRequest": {
        "method": "GET",
        "path": "/items"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"items\": [\"c\", \"d\"], \"page\": 2, \"hasMore\": true}"
    },
    "scenarioName": "Pagination",
    "scenarioState": "Page2",
    "newScenarioState": "Page3",
    "times": { "remainingTimes": 1 }
}

// Page 3 (last page)
{
    "httpRequest": {
        "method": "GET",
        "path": "/items"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"items\": [\"e\"], \"page\": 3, \"hasMore\": false}"
    },
    "scenarioName": "Pagination",
    "scenarioState": "Page3"
}</code></pre>

<p><strong>Example - Retry Testing:</strong> First two calls return 503, third call succeeds.</p>
<pre class="prettyprint lang-java code"><code class="code">// First call: 503
{
    "httpRequest": { "path": "/api/data" },
    "httpResponse": { "statusCode": 503, "body": "{\"error\": \"Service Unavailable\"}" },
    "scenarioName": "RetryTest",
    "scenarioState": "Started",
    "newScenarioState": "Attempt2",
    "times": { "remainingTimes": 1 }
}

// Second call: 503
{
    "httpRequest": { "path": "/api/data" },
    "httpResponse": { "statusCode": 503, "body": "{\"error\": \"Service Unavailable\"}" },
    "scenarioName": "RetryTest",
    "scenarioState": "Attempt2",
    "newScenarioState": "Ready",
    "times": { "remainingTimes": 1 }
}

// Third call onwards: 200
{
    "httpRequest": { "path": "/api/data" },
    "httpResponse": { "statusCode": 200, "body": "{\"data\": \"success\"}" },
    "scenarioName": "RetryTest",
    "scenarioState": "Ready"
}</code></pre>

<p><strong>Example - Order State Machine:</strong> Model an order lifecycle with state transitions.</p>
<pre class="prettyprint lang-java code"><code class="code">// Create order
{
    "httpRequest": { "method": "POST", "path": "/orders" },
    "httpResponse": { "statusCode": 201, "body": "{\"id\": 1, \"status\": \"CREATED\"}" },
    "scenarioName": "OrderLifecycle",
    "scenarioState": "Started",
    "newScenarioState": "OrderCreated",
    "times": { "remainingTimes": 1 }
}

// Pay for order (only available after creation)
{
    "httpRequest": { "method": "PUT", "path": "/orders/1/pay" },
    "httpResponse": { "statusCode": 200, "body": "{\"id\": 1, \"status\": \"PAID\"}" },
    "scenarioName": "OrderLifecycle",
    "scenarioState": "OrderCreated",
    "newScenarioState": "OrderPaid",
    "times": { "remainingTimes": 1 }
}

// Get order - returns CREATED status
{
    "httpRequest": { "method": "GET", "path": "/orders/1" },
    "httpResponse": { "statusCode": 200, "body": "{\"id\": 1, \"status\": \"CREATED\"}" },
    "scenarioName": "OrderLifecycle",
    "scenarioState": "OrderCreated"
}

// Get order - returns PAID status
{
    "httpRequest": { "method": "GET", "path": "/orders/1" },
    "httpResponse": { "statusCode": 200, "body": "{\"id\": 1, \"status\": \"PAID\"}" },
    "scenarioName": "OrderLifecycle",
    "scenarioState": "OrderPaid"
}</code></pre>

<p><strong>Example - Rate Limiting:</strong> Allow a fixed number of calls, then return 429.</p>
<pre class="prettyprint lang-java code"><code class="code">// First 3 calls succeed
{
    "httpRequest": { "path": "/api/resource" },
    "httpResponse": { "statusCode": 200, "body": "{\"data\": \"ok\"}" },
    "scenarioName": "RateLimit",
    "scenarioState": "Started",
    "newScenarioState": "Call2",
    "times": { "remainingTimes": 1 }
}
{
    "httpRequest": { "path": "/api/resource" },
    "httpResponse": { "statusCode": 200, "body": "{\"data\": \"ok\"}" },
    "scenarioName": "RateLimit",
    "scenarioState": "Call2",
    "newScenarioState": "Call3",
    "times": { "remainingTimes": 1 }
}
{
    "httpRequest": { "path": "/api/resource" },
    "httpResponse": { "statusCode": 200, "body": "{\"data\": \"ok\"}" },
    "scenarioName": "RateLimit",
    "scenarioState": "Call3",
    "newScenarioState": "RateLimited",
    "times": { "remainingTimes": 1 }
}

// Subsequent calls: 429 Too Many Requests
{
    "httpRequest": { "path": "/api/resource" },
    "httpResponse": { "statusCode": 429, "body": "{\"error\": \"Rate limit exceeded\"}" },
    "scenarioName": "RateLimit",
    "scenarioState": "RateLimited"
}</code></pre>

<a id="llm_conversation_example" class="anchor" href="#llm_conversation_example">&nbsp;</a>

<p><strong>Example - Multi-Turn LLM Conversation:</strong> Script a two-turn agent loop where the model first calls a tool, then answers after receiving the tool result. Each turn advances the scenario state automatically. Use <span class="inline_code">isolateBy</span> so concurrent sessions (identified by a header, query parameter, or cookie) maintain independent state.</p>

<pre class="prettyprint lang-java code"><code class="code">import static org.mockserver.client.LlmConversationBuilder.conversation;
import static org.mockserver.model.Completion.completion;
import static org.mockserver.model.ToolUse.toolUse;
import static org.mockserver.llm.IsolationSource.header;

conversation()
    .withPath("/v1/messages")
    .withProvider(Provider.ANTHROPIC)
    .withModel("claude-sonnet-4")
    .isolateBy(header("x-session-id"))
    .turn()
        .whenTurnIndex(0)
        .respondingWith(
            completion()
                .withToolCall(toolUse("search").withArguments("{\"query\":\"weather\"}"))
                .withStopReason("tool_use"))
    .andThen()
    .turn()
        .whenContainsToolResultFor("search")
        .respondingWith(
            completion()
                .withText("Based on the search results, it will be sunny.")
                .withStopReason("end_turn"))
    .applyTo(mockServerClient);</code></pre>

<p>JSON equivalent (two expectations sharing a scenario):</p>
<pre class="prettyprint lang-java code"><code class="code">// Turn 1: tool call
{
    "httpRequest": { "method": "POST", "path": "/v1/messages" },
    "httpLlmResponse": {
        "provider": "ANTHROPIC",
        "model": "claude-sonnet-4",
        "completion": {
            "toolCalls": [{ "name": "search", "arguments": "{\"query\":\"weather\"}" }],
            "stopReason": "tool_use"
        },
        "conversationPredicates": { "turnIndex": 0 }
    },
    "scenarioName": "__llm_conv_example",
    "scenarioState": "Started",
    "newScenarioState": "turn_1"
}

// Turn 2: final answer after tool result
{
    "httpRequest": { "method": "POST", "path": "/v1/messages" },
    "httpLlmResponse": {
        "provider": "ANTHROPIC",
        "model": "claude-sonnet-4",
        "completion": {
            "text": "Based on the search results, it will be sunny.",
            "stopReason": "end_turn"
        },
        "conversationPredicates": { "containsToolResultFor": "search" }
    },
    "scenarioName": "__llm_conv_example",
    "scenarioState": "turn_1",
    "newScenarioState": "__done"
}</code></pre>

<p>Scenarios are independent of each other — different scenario names maintain separate state. All scenario states are reset when MockServer is <a href="/mock_server/clearing_and_resetting.html">reset</a>.</p>

<a id="timed_scenario_flows" class="anchor" href="#timed_scenario_flows">&nbsp;</a>

<h4>Timed and Triggered Scenario Flows</h4>

<p>Beyond expectation-driven state transitions, MockServer supports <strong>timed auto-transitions</strong> and <strong>external triggers</strong> via REST endpoints. These let you model scenarios where state changes happen after a time delay (e.g. a background process completing) or are driven externally by a test harness.</p>

<p><strong>Get current state:</strong></p>
<pre class="prettyprint lang-bash code"><code class="code"># Returns {"scenarioName": "DeployFlow", "currentState": "Started"}
curl -X GET http://localhost:1080/mockserver/scenario/DeployFlow</code></pre>

<p><strong>Set state immediately:</strong></p>
<pre class="prettyprint lang-bash code"><code class="code"># Set the scenario to "Deploying" state
curl -X PUT http://localhost:1080/mockserver/scenario/DeployFlow \
  -H "Content-Type: application/json" \
  -d '{"state": "Deploying"}'</code></pre>

<p><strong>Set state with timed auto-transition:</strong> The scenario enters "Deploying" immediately, then automatically transitions to "Deployed" after 5 seconds (if still in "Deploying").</p>
<pre class="prettyprint lang-bash code"><code class="code">curl -X PUT http://localhost:1080/mockserver/scenario/DeployFlow \
  -H "Content-Type: application/json" \
  -d '{"state": "Deploying", "transitionAfterMs": 5000, "nextState": "Deployed"}'</code></pre>

<p><strong>External trigger:</strong> Advance the scenario to a specific state from your test harness or CI pipeline.</p>
<pre class="prettyprint lang-bash code"><code class="code"># Jump directly to "Failed" state
curl -X PUT http://localhost:1080/mockserver/scenario/DeployFlow/trigger \
  -H "Content-Type: application/json" \
  -d '{"newState": "Failed"}'</code></pre>

<p><strong>Example - Deployment Pipeline Flow:</strong> Combine expectation-based matching with timed transitions to model a deployment that takes time to complete.</p>
<pre class="prettyprint lang-java code"><code class="code">// Step 1: Register expectations for each state
// When Deploying, GET /status returns "in progress"
{
    "httpRequest": { "method": "GET", "path": "/status" },
    "httpResponse": { "statusCode": 200, "body": "{\"status\": \"deploying\"}" },
    "scenarioName": "DeployFlow",
    "scenarioState": "Deploying"
}

// When Deployed, GET /status returns "complete"
{
    "httpRequest": { "method": "GET", "path": "/status" },
    "httpResponse": { "statusCode": 200, "body": "{\"status\": \"complete\"}" },
    "scenarioName": "DeployFlow",
    "scenarioState": "Deployed"
}

// Step 2: Start the flow with a timed transition
// PUT /mockserver/scenario/DeployFlow
// Body: {"state": "Deploying", "transitionAfterMs": 3000, "nextState": "Deployed"}
//
// Now: GET /status returns "deploying"
// After 3 seconds: GET /status returns "complete"</code></pre>

<p>Timed transitions are guarded: the transition only fires if the scenario is still in the expected <span class="inline_code">currentState</span> when the timer expires. If the scenario has already moved to a different state (e.g. via an external trigger or expectation match), the timed transition becomes a no-op. Scheduling a new transition for the same scenario cancels any pending one.</p>

<a id="cross_protocol_scenario_correlation" class="anchor" href="#cross_protocol_scenario_correlation">&nbsp;</a>

<h4>Cross-Protocol Scenario Correlation</h4>

<p>Scenario state transitions are not limited to HTTP request matches. A DNS query, WebSocket connection, gRPC request, or HTTP request can drive a scenario forward via a <strong>cross-protocol trigger</strong>. This lets you model multi-protocol test flows — for example: "when a WebSocket connects, advance scenario X so the next HTTP expectation for that state becomes active."</p>

<p>Add a <span class="inline_code">crossProtocolScenarios</span> array to an expectation. Each entry specifies:</p>
<ul>
    <li><strong>trigger</strong> — the protocol event that fires the transition: <span class="inline_code">DNS_QUERY</span>, <span class="inline_code">WEBSOCKET_CONNECT</span>, <span class="inline_code">GRPC_REQUEST</span>, or <span class="inline_code">HTTP_REQUEST</span></li>
    <li><strong>matchPattern</strong> — optional substring filter on the event identifier (DNS query name, gRPC service name, HTTP path, or WebSocket URL); if omitted, all events of that type match</li>
    <li><strong>scenarioName</strong> — the name of the scenario to advance</li>
    <li><strong>targetState</strong> — the state to transition the scenario to</li>
</ul>

<p><strong>Example — WebSocket connect advances an HTTP scenario:</strong> When any WebSocket connection is established, the "ConnectionFlow" scenario transitions to "WsConnected", activating the HTTP expectation that requires that state.</p>

<pre class="prettyprint lang-java code"><code class="code">// Expectation 1: dummy WebSocket endpoint that fires the cross-protocol transition
{
    "httpRequest": {
        "method": "GET",
        "path": "/ws/events"
    },
    "httpResponse": {
        "statusCode": 101
    },
    "crossProtocolScenarios": [
        {
            "trigger": "WEBSOCKET_CONNECT",
            "scenarioName": "ConnectionFlow",
            "targetState": "WsConnected"
        }
    ]
}

// Expectation 2: only active after the WebSocket has connected
{
    "httpRequest": {
        "method": "GET",
        "path": "/api/status"
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"status\": \"connected\"}"
    },
    "scenarioName": "ConnectionFlow",
    "scenarioState": "WsConnected"
}</code></pre>

<p><strong>Example — DNS query activates a downstream-failure scenario:</strong> When a DNS query for <span class="inline_code">payments.svc</span> is observed, advance the scenario to "DnsObserved" so a chaos-injecting HTTP expectation becomes active.</p>

<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": { "method": "GET", "path": "/_internal/dns-trigger" },
    "httpResponse": { "statusCode": 200 },
    "crossProtocolScenarios": [
        {
            "trigger": "DNS_QUERY",
            "matchPattern": "payments.svc",
            "scenarioName": "PaymentsFlow",
            "targetState": "DnsObserved"
        }
    ]
}</code></pre>

<p>The <span class="inline_code">matchPattern</span> is a substring match against the event identifier. For <span class="inline_code">DNS_QUERY</span> it is the query name; for <span class="inline_code">GRPC_REQUEST</span> it is the gRPC service name; for <span class="inline_code">HTTP_REQUEST</span> it is the request path; for <span class="inline_code">WEBSOCKET_CONNECT</span> it is the WebSocket URL. When <span class="inline_code">matchPattern</span> is omitted, all events of that trigger type match. Multiple entries in <span class="inline_code">crossProtocolScenarios</span> are evaluated independently — a single event can advance several scenarios in one fire.</p>

<a id="sequential_responses" class="anchor" href="#sequential_responses">&nbsp;</a>

<h4>Sequential / Cycling Responses</h4>

<p>A single expectation can return <strong>multiple different responses</strong> by providing an array of responses in the <span class="inline_code">httpResponses</span> field. Each time the expectation matches, the next response in the list is returned. After the last response, it cycles back to the first.</p>

<p>The <span class="inline_code">responseMode</span> field controls how responses are selected:</p>
<ul>
    <li><strong>SEQUENTIAL</strong> (default) - responses are returned in order, cycling back to the first after the last</li>
    <li><strong>RANDOM</strong> - a random response is selected from the list for each request</li>
</ul>

<p><strong>Note:</strong> Use <span class="inline_code">httpResponses</span> instead of <span class="inline_code">httpResponse</span> (singular) when you want multiple responses. If both are set, <span class="inline_code">httpResponses</span> takes priority.</p>

<p><strong>Example - Sequential Responses:</strong> Cycle through different status codes.</p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "path": "/api/status"
    },
    "httpResponses": [
        { "statusCode": 200, "body": "{\"status\": \"ok\"}" },
        { "statusCode": 503, "body": "{\"status\": \"degraded\"}" },
        { "statusCode": 200, "body": "{\"status\": \"ok\"}" }
    ]
}</code></pre>
<p>The first request returns 200, the second returns 503, the third returns 200, then it cycles back to the first response.</p>

<p><strong>Example - Random Responses:</strong> Simulate unpredictable service behavior.</p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "path": "/api/unstable"
    },
    "httpResponses": [
        { "statusCode": 200, "body": "{\"result\": \"success\"}" },
        { "statusCode": 500, "body": "{\"error\": \"Internal Server Error\"}" },
        { "statusCode": 429, "body": "{\"error\": \"Too Many Requests\"}" }
    ],
    "responseMode": "RANDOM"
}</code></pre>

<p><strong>Example - Java Client:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;

new MockServerClient("localhost", 1080)
    .when(request().withPath("/api/status"))
    .respond(
        java.util.Arrays.asList(
            response().withStatusCode(200).withBody("{\"status\": \"ok\"}"),
            response().withStatusCode(503).withBody("{\"status\": \"degraded\"}")
        )
    );</code></pre>

<a id="updating_expectations" class="anchor" href="#updating_expectations">&nbsp;</a>

<h4>Updating Expectations</h4>

<p>If an expectation is added and the <strong>id</strong> field matches an existing expectation the existing expectation will be updated (i.e. replaced). A UUID will be used assigned to each expectation if no value for <strong>id</strong>
    is specified.</p>

<p></p>

<a id="request_matchers" class="anchor" href="#request_matchers">&nbsp;</a>

<h3>Request Matchers</h3>

<p>There are two types of <strong>request matcher</strong>:</p>
<ul>
    <li><strong><a href="#request_properties_matchers">request properties matcher</a></strong> - that match requests using HTTP properties such as <strong>method</strong>, <strong>path</strong> or <strong>body</strong></li>
    <li><strong><a href="#request_openapi_matchers">open api request matcher</a></strong> - that match requests using an <a target="_blank" href="https://swagger.io/docs/specification/about/" rel="noopener noreferrer"><strong>OpenAPI</strong></a> definition</li>
</ul>

<a id="request_properties_matchers" class="anchor" href="#request_properties_matchers">&nbsp;</a>

<p>A <strong>request properties matcher</strong> matches requests using one or more of the following properties:</p>

<ul>
    <li><strong>method</strong> - <a href="#request_property_matchers">property matcher</a></li>
    <li><strong>path</strong> - <a href="#request_property_matchers">property matcher</a></li>
    <li><strong>path parameters</strong> - <a href="#request_key_to_multivalue_matchers">key to multiple values matcher</a></li>
    <li><strong>query string parameters</strong> - <a href="#request_key_to_multivalue_matchers">key to multiple values matcher</a></li>
    <li><strong>headers</strong> - <a href="#request_key_to_multivalue_matchers">key to multiple values matcher</a></li>
    <li><strong>cookies</strong> - <a href="#request_key_to_value_matchers">key to single value matcher</a></li>
    <li><strong>body</strong> - <a href="#request_body_matchers">body matchers</a></li>
    <li><strong>secure</strong> - boolean value, true for HTTPS</li>
</ul>

<a id="request_property_matchers" class="anchor" href="#request_property_matchers">&nbsp;</a>

<p>Matching for <strong>properties</strong> can be done using:</p>

<ul>
    <li><strong>string value</strong>
        <ul>
            <li><strong>use for:</strong> method, path, path parameter keys, path parameters values, query parameter keys, query parameters values, header keys, header values, cookie keys, cookie values or bodies</li>
            <li><strong>examples:</strong> <a href="#button_match_request_by_query_parameter_name_regex">method</a>, <a href="#button_match_request_by_path">path</a>, <a href="#button_match_request_by_path_and_path_parameters">path parameters</a>, <a
                    href="#button_match_request_by_cookies_and_query_parameters">query parameters</a>, <a href="#button_match_request_by_headers">headers</a>, <a href="#button_match_request_by_cookies_and_query_parameters">cookies</a></li>
        </ul>
    </li>
    <li><strong>regex value</strong>
        <ul>
            <li><strong>use for:</strong> method, path, path parameter keys, path parameters values, query parameter keys, query parameters values, header keys, header values, cookie keys, cookie values or bodies</li>
            <li><strong>examples:</strong> <a href="#button_match_request_by_method_regex">method</a>, <a href="#button_match_request_by_regex_path">path</a>, <a href="#button_match_request_by_path_parameter_regex_value">path parameters</a>, <a
                    href="#button_match_request_by_query_parameter_name_regex">query parameters</a>, <a href="#button_match_request_by_header_name_regex">headers</a></li>
            <li>for syntax see <a target="_blank" href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html" rel="noopener noreferrer">Java regex syntax</a></li>
        </ul>
    </li>
    <li><strong>json schema</strong>
        <ul>
            <li><strong>use for:</strong> method, path, path parameters values, query parameters values, header values, cookie values or bodies</li>
            <li><strong>not supported for:</strong> path parameter keys, query parameter keys, header keys or cookie keys</li>
            <li><strong>examples:</strong> <a href="#button_match_request_by_path_parameter_json_schema_value">path parameters</a>, <a href="#button_match_request_by_query_parameter_value_json_schema">query parameters</a>, <a href="#button_match_request_by_header_value_json_schema">headers</a> , <a href="#button_match_request_by_cookies_value_json_schema">cookies</a></li>
            <li>for syntax see <a target="_blank" href="https://json-schema.org" rel="noopener noreferrer">JSON Schema documentation</a></li>
        </ul>
    </li>
    <li><strong>optional value</strong>
        <ul>
            <li><strong>use for:</strong> method, path, path parameters values, query parameter keys, query parameters values, header keys, header values, cookie keys, cookie values or bodies</li>
            <li><strong>not supported for:</strong> path parameter keys</li>
            <li><strong>examples:</strong> <a href="#button_match_request_by_optional_query_parameter">query parameters</a>, <a href="#button_match_request_by_optional_header">headers</a>, <a href="#button_match_request_by_optional_cookies">cookies</a></li>
        </ul>
    </li>
    <li><strong>negated value</strong>
        <ul>
            <li><strong>use for:</strong> method, path, path parameter keys, path parameters values, query parameter keys, query parameters values, header keys, header values, cookie keys, cookie values or bodies</li>
            <li><strong>examples:</strong> <a href="#button_match_request_by_not_matching_method">method</a>, <a href="#button_match_request_by_not_matching_path">path</a>, <a href="#button_match_request_by_not_matching_header_value">headers</a>
            </li>
        </ul>
    </li>
</ul>

<a id="path_matching_curly_braces" class="anchor" href="#path_matching_curly_braces">&nbsp;</a>

<p><strong>Note:</strong> path values containing <span class="inline_code">{</span> or <span class="inline_code">}</span> characters (such as <span class="inline_code">/api/{id}</span>) are interpreted as <strong>regex patterns</strong>, not literal strings. This is a common source of unexpected matching behaviour when path templates are used. To match literal curly braces in a path, use one of the following approaches:</p>

<ul>
    <li>Use an explicit regex matcher and escape the braces: <span class="inline_code">withPath(regex("/api/\\{id\\}"))</span></li>
    <li>Use an exact string matcher: <span class="inline_code">withPath(exact("/api/{id}"))</span></li>
</ul>

<a id="request_key_to_multivalue_matchers" class="anchor" href="#request_key_to_multivalue_matchers">&nbsp;</a>

<p>Matching for <strong>key to multiple values</strong> supports multiple values for each key for headers, query parameters and path parameters</p>

<ul>
    <li>Keys support all <a href="#request_property_matchers">property matcher</a> except json schema</li>
    <li>Values support all <a href="#request_property_matchers">property matcher</a> except optional values</li>
    <li>Matching supports two modes:
        <ul>
            <li><strong><a href="#button_match_request_by_query_parameter_by_sub_set">sub set</a></strong> (default) - matches if the request property contains a matching sub set (considering optional keys), therefore there is <strong>at least one matching value</strong> for each
                non-optional key or optional key if present
            </li>
            <li><strong><a href="#button_match_request_by_header_by_matching_key">matching key</a></strong> - matches if the request property contains only matching values (considering optional keys), therefore <strong>all values must match</strong> for each non-optional key or optional
                key if present
            </li>
        </ul>
    </li>
</ul>

<p><strong>Note:</strong> for query parameters, the default <strong>sub set</strong> matching mode means that extra query parameters not specified in the matcher are allowed and do not cause a match failure. To enforce strict matching where <strong>only</strong> the specified query parameters are allowed (and any additional parameters cause a mismatch), use <span class="inline_code">KeyMatchStyle.MATCHING_KEY</span> on the request matcher. For example:</p>

<pre class="prettyprint lang-java code"><code class="code">request()
    .withPath("/some/path")
    .withQueryStringParameters(
        new Parameters(
            param("key", "value")
        ).withKeyMatchStyle(KeyMatchStyle.MATCHING_KEY)
    )</code></pre>

<a id="request_key_to_value_matchers" class="anchor" href="#request_key_to_value_matchers">&nbsp;</a>

<p>Matching for <strong>key to single value</strong> supports a single value for each key for cookies</p>

<ul>
    <li>Keys support all <a href="#request_property_matchers">property matcher</a> except json schema</li>
    <li>Values support all <a href="#request_property_matchers">property matcher</a> except optional values</li>
</ul>

<a id="request_body_matchers" class="anchor" href="#request_body_matchers">&nbsp;</a>

<a id="body_matching_string_vs_json" class="anchor" href="#body_matching_string_vs_json">&nbsp;</a>

<p><strong>Important:</strong> when matching JSON bodies, there is a significant difference between plain string matching and semantic JSON matching:</p>

<ul>
    <li><span class="inline_code">withBody("{\"key\": \"value\"}")</span> performs a <strong>character-by-character string comparison</strong>. This means whitespace, key ordering, and formatting must match exactly. For example, <span class="inline_code">{"key":"value"}</span> would <strong>not</strong> match <span class="inline_code">{"key": "value"}</span> because of the missing space.</li>
    <li><span class="inline_code">withBody(json("{\"key\": \"value\"}"))</span> performs a <strong>semantic JSON comparison</strong>. This ignores whitespace and (with <span class="inline_code">ONLY_MATCHING_FIELDS</span>) allows additional fields. Use this for matching JSON payloads.</li>
</ul>

<p>If your expectations are not matching JSON requests as expected, ensure you are using <span class="inline_code">json()</span> rather than passing the JSON string directly.</p>

<p>Matching for <strong>bodies</strong> can be done using:</p>

<ul>
    <li><strong><a href="#button_match_request_by_body_in_utf16">plain text</a></strong> (i.e. exact match)</li>
    <li><strong><a href="#button_match_request_by_regex_body">regular expression</a></strong> - see <a target="_blank" href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html" rel="noopener noreferrer">Java regex syntax</a></li>
    <li><strong><a href="#button_match_request_by_body_with_json_exactly">JSON</a></strong> - supports:
        <ul>
            <li><strong>matchType</strong> to control which fields get matched:
                <ul>
                    <li><strong><a href="#button_match_request_by_body_with_json_exactly">STRICT</a></strong> matches all fields, order of arrays and no additional fields allowed</li>
                    <li><strong><a href="#button_match_request_by_body_with_json_ignoring_extra_fields">ONLY_MATCHING_FIELDS</a></strong> only matches fields provided in the request matcher</li>
                </ul>
            </li>
            <li><strong><a href="#button_match_request_by_body_with_json_lax_numbers">matchNumbersAsStrings</a></strong> (optional, default <strong>false</strong>) if <strong>true</strong>, numeric values are compared by numeric value regardless of type, so <strong>1</strong>, <strong>1.0</strong> and <strong>1.00</strong> are all considered equal. This is useful when different languages or serializers produce different numeric representations for the same value (e.g. Java sends <strong>1.0</strong> while Groovy sends <strong>1</strong>).</li>
            <li><strong><a href="#button_match_request_by_body_with_json_placeholders">JSONUnit placeholders</a></strong> to allow fields or values to be ignored or matched by type or pattern, for example:
                <ul>
                    <li><strong>${json-unit.ignore-element}</strong> ignore a field</li>
                    <li><strong>${json-unit.any-boolean}</strong> match a field as any boolean</li>
                    <li><strong>${json-unit.any-string}</strong> match a field as any string</li>
                    <li><strong>${json-unit.any-number}</strong> match a field as any number</li>
                    <li><strong>${json-unit.regex}<em>pattern</em></strong> match a field by regex, e.g. <strong>${json-unit.regex}[a-z]+</strong></li>
                    <li><strong>${json-unit.matches:<em>name</em>}</strong> match a field with a user-registered custom matcher (e.g. <strong>${json-unit.matches:largerThan}</strong>); register the matcher by setting the <a href="/mock_server/configuration_properties.html#button_configuration_custom_json_unit_matchers_class"><strong>mockserver.customJsonUnitMatchersClass</strong></a> configuration property to a class implementing <strong>CustomJsonUnitMatcherProvider</strong></li>
                </ul>
                see <a target="_blank" href="https://github.com/lukas-krecan/JsonUnit" rel="noopener noreferrer">JSONUnit documentation</a> for full details
            </li>
        </ul>
    </li>
    <li><strong><a href="#button_match_request_by_body_with_json_schema">JSON Schema</a></strong> - see <a target="_blank" href="https://json-schema.org/" rel="noopener noreferrer">JSON Schema documentation</a></li>
    <li><strong><a href="#button_match_request_by_body_with_json_path">JsonPath</a></strong> - matches if at least one value is returned by the expression, see <a target="_blank" href="https://github.com/json-path/JsonPath" rel="noopener noreferrer">JsonPath documentation</a></li>
    <li><strong><a href="#button_match_request_by_body_with_xml">XML</a></strong> - supports:
        <ul>
            <li><strong><a href="#button_match_request_by_body_with_xml_placeholders">XMLUnit placeholders</a></strong> to allow fields or elements to be ignored or matched by type for example:
                <ul>
                    <li><strong>${xmlunit.ignore}</strong> ignore an element</li>
                    <li><strong>${xmlunit.isNumber}</strong> match an element or attribute as a number</li>
                </ul>
                see <a target="_blank" href="https://github.com/xmlunit/user-guide/wiki/Placeholders" rel="noopener noreferrer">XMLUnit documentation</a> for full details
            </li>
        </ul>
    </li>
    <li><strong><a href="#button_match_request_by_body_with_xml_schema">XML Schema</a></strong> - see <a target="_blank" href="https://www.w3.org/standards/xml/schema.html" rel="noopener noreferrer">XML Schema documentation</a></li>
    <li><strong><a href="#button_match_request_by_body_with_xpath">XPath</a></strong> - matches if at least one value returned by expression, see <a target="_blank" href="https://www.w3.org/TR/2017/REC-xpath-31-20170321/" rel="noopener noreferrer">XPath specification</a></li>
    <li><strong><a href="#button_match_request_by_form_submission_body">form fields</a></strong> (i.e. body parameters)</li>
    <li><strong><a href="#button_match_request_by_binary_body">binary</a></strong></li>
    <li><strong><a href="#graphql_body_matching">GraphQL</a></strong> - whitespace-insensitive matching of GraphQL queries, mutations, and subscriptions</li>
    <li><strong><a href="#button_match_request_by_not_matching_body_with_xpath">negated matcher</a></strong></li>
</ul>

<a id="graphql_body_matching" class="anchor" href="#graphql_body_matching">&nbsp;</a>

<h4>GraphQL Body Matching</h4>

<p>MockServer supports whitespace-insensitive matching of <a target="_blank" href="https://graphql.org/learn/serving-over-http/" rel="noopener noreferrer">GraphQL over HTTP</a> JSON request bodies. This allows expectations to match GraphQL queries, mutations, and subscriptions regardless of whitespace, formatting, or comment differences. The request body must be a JSON object with a <span class="inline_code">query</span> field (the standard GraphQL over HTTP format).</p>

<p>A GraphQL body matcher can specify:</p>
<ul>
    <li><strong>query</strong> (required) - the GraphQL query string. Whitespace is normalized before comparison, so <span class="inline_code">{ user { name } }</span> matches <span class="inline_code">{\n  user {\n    name\n  }\n}</span></li>
    <li><strong>operationName</strong> (optional) - match the operation name exactly, or using a regex pattern (e.g. <span class="inline_code">Get.*</span>)</li>
    <li><strong>variablesSchema</strong> (optional) - a <a target="_blank" href="https://json-schema.org/" rel="noopener noreferrer">JSON Schema</a> to validate the <span class="inline_code">variables</span> field</li>
    <li><strong>selectionSetMatchType</strong> (optional) - controls how the selection set (fields) is compared. Values:
        <ul>
            <li><span class="inline_code">NORMALISED_STRING</span> (default) - whitespace-normalised string comparison of the full query</li>
            <li><span class="inline_code">AST_EXACT</span> - matches operation type, name, and the exact set of top-level field names (ignores whitespace, nested fields, and argument differences)</li>
            <li><span class="inline_code">AST_SUBSET</span> - matches when the expected top-level fields are a subset of the actual request's fields (useful when the request may contain additional fields)</li>
        </ul>
    </li>
    <li><strong>fields</strong> (optional) - explicit list of top-level field names to match in AST modes. If not specified, fields are extracted from the <span class="inline_code">query</span> string</li>
</ul>

<p><strong>Example - match a GraphQL query:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/graphql",
        "body": {
            "type": "GRAPHQL",
            "query": "{ user(id: 1) { name email } }"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"data\": {\"user\": {\"name\": \"Alice\", \"email\": \"alice@example.com\"}}}"
    }
}</code></pre>

<p><strong>Example - match with operation name and variables schema:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/graphql",
        "body": {
            "type": "GRAPHQL",
            "query": "query GetUser($id: ID!) { user(id: $id) { name email } }",
            "operationName": "GetUser",
            "variablesSchema": "{\"type\": \"object\", \"properties\": {\"id\": {\"type\": \"string\"}}, \"required\": [\"id\"]}"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"data\": {\"user\": {\"name\": \"Alice\", \"email\": \"alice@example.com\"}}}"
    }
}</code></pre>

<p><strong>Example - match a mutation:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/graphql",
        "body": {
            "type": "GRAPHQL",
            "query": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }",
            "operationName": "CreateUser"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"data\": {\"createUser\": {\"id\": \"123\", \"name\": \"Alice\"}}}"
    }
}</code></pre>

<p><strong>Example - AST subset matching (match any query containing a "users" field):</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/graphql",
        "body": {
            "type": "GRAPHQL",
            "query": "query { users { id } }",
            "selectionSetMatchType": "AST_SUBSET",
            "fields": ["users"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"data\": {\"users\": [{\"id\": \"1\", \"name\": \"Alice\"}]}}"
    }
}</code></pre>

<p><strong>Example - AST exact matching (match a query with exactly these top-level fields):</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/graphql",
        "body": {
            "type": "GRAPHQL",
            "query": "query GetDashboard { user profile settings }",
            "selectionSetMatchType": "AST_EXACT"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"data\": {\"user\": {}, \"profile\": {}, \"settings\": {}}}"
    }
}</code></pre>

<a id="graphql_subscriptions" class="anchor" href="#graphql_subscriptions">&nbsp;</a>

<h4>GraphQL Subscriptions over WebSocket</h4>

<p>MockServer supports mocking <a target="_blank" href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md" rel="noopener noreferrer">GraphQL subscriptions over WebSocket</a> using the <span class="inline_code">graphql-transport-ws</span> protocol (also accepts the legacy <span class="inline_code">graphql-ws</span> subprotocol). This allows testing GraphQL subscription clients without a real GraphQL server.</p>

<p>The protocol flow is:</p>
<ol>
    <li>Client opens a WebSocket connection with <span class="inline_code">Sec-WebSocket-Protocol: graphql-transport-ws</span></li>
    <li>Client sends <span class="inline_code">connection_init</span>, MockServer replies <span class="inline_code">connection_ack</span></li>
    <li>Client sends <span class="inline_code">subscribe</span> with a GraphQL query; MockServer AST-matches the query against the configured filter</li>
    <li>On match: MockServer pushes a sequence of <span class="inline_code">next</span> messages (with optional delays), then <span class="inline_code">complete</span></li>
    <li>On no match: MockServer sends an <span class="inline_code">error</span> message</li>
    <li>Client can send <span class="inline_code">complete</span> to cancel a subscription</li>
    <li><span class="inline_code">ping</span>/<span class="inline_code">pong</span> keepalive is supported</li>
</ol>

<p>To configure a GraphQL subscription mock, use an <span class="inline_code">httpWebSocketResponse</span> with:</p>
<ul>
    <li><strong>subprotocol</strong> - set to <span class="inline_code">graphql-transport-ws</span> (or <span class="inline_code">graphql-ws</span>)</li>
    <li><strong>graphqlSubscriptionFilter</strong> - a GraphQL body filter specifying the subscription query to match. Supports the same <span class="inline_code">selectionSetMatchType</span> options as <a href="#graphql_body_matching">GraphQL body matching</a></li>
    <li><strong>messages</strong> - the sequence of payloads to push as <span class="inline_code">next</span> events. Each message's <span class="inline_code">text</span> is wrapped in the protocol envelope <span class="inline_code">{"id":"...","type":"next","payload":{"data":...}}</span></li>
</ul>

<p><strong>Example - mock a GraphQL subscription that pushes two events:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "GET",
        "path": "/graphql"
    },
    "httpWebSocketResponse": {
        "subprotocol": "graphql-transport-ws",
        "graphqlSubscriptionFilter": {
            "query": "subscription { userUpdated { id name } }",
            "selectionSetMatchType": "AST_SUBSET"
        },
        "messages": [
            {"text": "{\"id\": \"1\", \"name\": \"Alice\"}"},
            {"text": "{\"id\": \"2\", \"name\": \"Bob\"}", "delay": {"timeUnit": "MILLISECONDS", "value": 500}}
        ]
    }
}</code></pre>

<p><strong>Example - Java API:</strong></p>
<pre class="prettyprint lang-java code"><code class="code">mockServerClient.when(
    request()
        .withMethod("GET")
        .withPath("/graphql")
).respondWithWebSocket(
    webSocketResponse()
        .withSubprotocol("graphql-transport-ws")
        .withGraphqlSubscriptionFilter(
            GraphQLBody.graphQL("subscription { userUpdated { id name } }")
                .withSelectionSetMatchType(SelectionSetMatchType.AST_SUBSET)
        )
        .withMessage(webSocketMessage("{\"id\": \"1\", \"name\": \"Alice\"}"))
        .withMessage(webSocketMessage("{\"id\": \"2\", \"name\": \"Bob\"}"))
);</code></pre>

<p>When a client connects and sends:</p>
<pre class="prettyprint lang-java code"><code class="code">{"type": "connection_init"}
// MockServer replies: {"type": "connection_ack"}

{"id": "1", "type": "subscribe", "payload": {"query": "subscription { userUpdated { id name } }"}}
// MockServer replies:
// {"id": "1", "type": "next", "payload": {"data": {"id": "1", "name": "Alice"}}}
// {"id": "1", "type": "next", "payload": {"data": {"id": "2", "name": "Bob"}}}
// {"id": "1", "type": "complete"}</code></pre>

<p><strong>Note:</strong> The legacy <span class="inline_code">graphql-ws</span> subprotocol (used by the older <a target="_blank" href="https://github.com/apollographql/subscriptions-transport-ws" rel="noopener noreferrer">subscriptions-transport-ws</a> library) is also accepted. Both use the same message format in MockServer's implementation.</p>

<button id="button_request" class="accordion title"><strong>Request Properties Matcher Code Examples</strong></button>
<div class="panel title">
    {% include_subpage _includes/request_matcher_code_examples.html %}
</div>

<a id="request_openapi_matchers" class="anchor" href="#request_openapi_matchers">&nbsp;</a>

<p>An <strong>open api request matcher</strong> can contain any of the following fields:</p>

<ul>
    <li><strong>specUrlOrPayload</strong> - mandatory value containing an <a target="_blank" href="https://swagger.io/docs/specification/basic-structure/" rel="noopener noreferrer"><strong>OpenAPI v3</strong></a> specifications in either JSON or YAML format as an:
        <ul>
            <li><strong><a href="#button_match_by_openapi_url">HTTP/HTTPS URL</a></strong></li>
            <li><strong><a href="#button_match_by_openapi_filepath">File URL</a></strong></li>
            <li><strong><a href="#button_match_by_openapi_classpath">classpath location</a></strong> (without the classpath: scheme)</li>
            <li><strong><a href="#button_match_by_inline_openapi_json">inline JSON object</a></strong></li>
            <li><strong><a href="#button_match_by_inline_openapi_yaml">inline escaped YAML string</a></strong></li>
        </ul>
    </li>
    <li><strong>operationId</strong> - optional value that specifies which <a href="#button_match_by_openapi_url_and_operation">operation</a> to match against, if <a href="#button_match_by_openapi_url">empty or null</a> all operations are
        matched against
    </li>
</ul>

<p>MockServer creates a set of <strong><a href="#request_properties_matchers">request properties matchers</a></strong> for each <strong>open api request matcher</strong>, to ensures control-plane logic such as <a
        href="/mock_server/clearing_and_resetting.html">clearing expectations</a> or <a href="/mock_server/debugging_issues.html">retrieving expectations</a> work consistently between the two types of request matchers, this can be viewed in
    the <a href="/mock_server/mockserver_ui.html">MockServer UI</a> <a href="/mock_server/mockserver_ui.html#active_expectations">active expectations</a> section.</p>

<button id="button_match_by_openapi" class="accordion title"><strong>OpenAPI Request Matcher Code Examples</strong></button>
<div class="panel title">
    {% include_subpage _includes/openapi_request_matcher_code_examples.html %}
</div>

<a id="actions" class="anchor" href="#actions">&nbsp;</a>

<h3>Actions</h3>

<p>Actions can be one of the following types:</p>
<ul>
    <li><strong><a href="#response_action">response</a></strong> - returns a response defined by using
        <ul>
            <li>a <strong><a href="#button_response_literal_body_only">literal</a></strong></li>
            <li>a <strong><a href="#button_javascript_templated_response">javascript template</a></strong></li>
            <li>a <strong><a href="#button_javascript_velocity_templated_response">velocity template</a></strong></li>
            <li>a <strong><a href="#button_response_class_callback">class callback</a></strong> (must be present in classpath)</li>
            <li>a <strong><a href="#button_response_method_or_closure_callback">method / closure callback</a></strong> (via WebSocket)</li>
        </ul>
    </li>
    <li><strong><a href="#forward_action">forward</a></strong> - forwards modified requests and returns modified response by using
        <ul>
            <li>the <strong><a href="#button_forward_exactly">exact request and response received</a></strong></li>
            <li>an <strong><a href="#button_forward_overridden">static overridden request and / or response</a></strong></li>
            <li>a <strong><a href="#button_forward_overridden_and_modified_req">dynamically modified request and / or response</a></strong></li>
            <li>a <strong><a href="#button_javascript_templated_forward">javascript template (request only)</a></strong></li>
            <li>a <strong><a href="#button_javascript_velocity_templated_forward">velocity template (request only)</a></strong></li>
            <li>a <strong><a href="#button_forward_class_callback">class callback for request and / or response</a></strong> (must be present in classpath)</li>
            <li>a <strong><a href="#button_forward_method_or_closure_callback">method / closure callback for request and / or response</a></strong> (via WebSocket)</li>
        </ul>
    </li>
    <li><strong><a href="#error_action">error</a></strong> - returns an invalid response as a sequence of bytes or closes the connection</li>
    <li><strong><a href="#llm_response_action">LLM response</a></strong> - returns a provider-correct LLM completion (Anthropic, OpenAI, etc.) from a high-level description of text, tool calls, and usage</li>
</ul>

<p>If no action is present for a request because no <a href="#request_matchers">request matcher</a> was matched then:</p>
<ul>
    <li>if MockServer is being used as a proxy the request is proxied to its destination un-modified</li>
    <li>if the request host header does not match the hostname or IP the request automatically proxied to the host header</li>
</ul>

<a id="response_action" class="anchor" href="#response_action">&nbsp;</a>

<p>A <strong>response action</strong> can be:</p>

<ul>
    <li>
        <p>either a <a href="#button_response_literal_body_only">response literal</a> containing any of the following:</p>
        <ul>
            <li><strong><a href="#button_response_literal_status_code_and_reason_phrase">status code</a></strong></li>
            <li><strong><a href="#button_response_literal_status_code_and_reason_phrase">reason phrase</a></strong></li>
            <li><strong><a href="#button_response_literal_binary_PNG_body">body</a></strong></li>
            <li><strong><a href="#button_response_literal_with_header">headers</a></strong></li>
            <li><strong><a href="#button_response_literal_with_cookie">cookies</a></strong></li>
            <li><strong><a href="#button_response_literal_with_10_second_delay">delay</a></strong> (including <a href="#button_response_with_distribution_delay">distribution-based delays</a> for realistic latency simulation)</li>
            <li><strong><a href="#button_response_literal_with_connection_options_to_suppress_headers">connectionOptions</a></strong> that can be used to <a href="#button_response_literal_with_connection_options_to_suppress_headers">suppress
                headers</a>, <a href="#button_response_literal_with_connection_options_to_override_headers">override headers</a>, <a href="#button_response_literal_with_connection_options_to_close_socket">close the socket connection</a>, or <a href="#button_response_literal_with_chunked_dribble_delay">deliver the response in chunks with delays</a> (dribble delay)
            </li>
        </ul>
    </li>
    <li>
        <p>or a <strong>templated</strong> response using <strong><a href="#button_javascript_templated_response">javascript</a></strong> or <strong><a href="#button_javascript_velocity_templated_response">velocity</a></strong> with a
            <strong><a href="#button_javascript_templated_response_with_delay">delay</a></strong></p>
    </li>
    <li>
        <p>or a <strong>callback</strong> used to dynamically generate a response based on the request:</p>
        <ul>
            <li>
                <p>as a <strong>server side callback</strong> implemented as a <a href="#button_response_class_callback">java class</a> that has a default constructor, implements <span class="annotation">org.mockserver.mock.action.ExpectationResponseCallback</span>
                    and is available on the classpath</p>
            </li>
            <li>
                <p>as a <strong>client side callback</strong> implemented as a <a href="#button_response_method_or_closure_callback">closure</a> using the java or javascript clients</p>
            </li>
        </ul>
    </li>
</ul>

<button id="button_response" class="accordion title"><strong>Response Action Code Examples</strong></button>
<div class="panel title">
    {% include_subpage _includes/response_action_code_examples.html %}
</div>

<a id="forward_action" class="anchor" href="#forward_action">&nbsp;</a>

<p>A <strong>forward action</strong> can be:</p>

<ul>
    <li>
        <p>either an <a href="#button_forward_exactly">exact forwarder</a>, that forwards requests exactly as it receives them, containing the following:</p>
        <ul>
            <li><strong>host</strong></li>
            <li><strong>port</strong></li>
            <li><strong><a href="#button_forward_exactly_in_https">scheme</a></strong></li>
        </ul>
    </li>
    <li>
        <p>or an <a href="#button_forward_overridden">overridden request</a> (or overridden response), with a <strong><a href="#button_forward_overridden_with_delay">delay</a></strong> (including <a href="#button_forward_overridden_with_distribution_delay">distribution-based delays</a>), that allows any part of a forwarded request or
            response to be replaced or certain fields (path, headers, cookies or query parameters) to be modified</p>
    </li>
    <li>
        <p>or a templated forwarder using <strong><a href="#button_javascript_templated_forward">javascript</a></strong> or <strong><a href="#button_javascript_velocity_templated_forward">velocity</a></strong>, with a <strong><a
                href="#button_javascript_templated_forward_with_delay">delay</a></strong>, that allows requests to be modified or completely re-written before they are forwarded</p>
    </li>
    <li>
        <p>or a <strong>callback</strong> used to dynamically generate the request to forward based on the request received by MockServer:</p>
        <ul>
            <li>
                <p>as a <strong>server side callback</strong> implemented as a <a href="#button_forward_class_callback">java class</a> that has a default constructor, implements <span class="annotation">org.mockserver.mock.action.ExpectationForwardCallback</span>
                    or <span class="annotation">org.mockserver.mock.action.ExpectationForwardAndResponseCallback</span> and is available on the classpath</p>
            </li>
            <li>
                <p>as a <strong>client side callback</strong> implemented as a <a href="#button_forward_method_or_closure_callback">closure</a> using the java or javascript clients</p>
            </li>
        </ul>
    </li>
</ul>

<button id="button_forward" class="accordion title"><strong>Forward Action Code Examples</strong></button>
<div class="panel title">
    {% include_subpage _includes/forward_action_code_examples.html %}
</div>

<a id="forward_with_fallback_action" class="anchor" href="#forward_with_fallback_action">&nbsp;</a>

<h3>Forward with Fallback</h3>

<p>A <strong>forward with fallback action</strong> (<span class="inline_code">httpForwardWithFallback</span>) forwards the request to an upstream service, but returns a pre-configured fallback response when the upstream returns an error status code or the connection fails. This is unique to MockServer's hybrid mock+proxy architecture.</p>

<p>Use cases:</p>
<ul>
    <li><strong>Resilience testing</strong> - test your application's behaviour when a downstream service is flaky</li>
    <li><strong>Development against partially-available services</strong> - work offline with cached/mock responses while still connecting to real services when they are available</li>
    <li><strong>Circuit breaker simulation</strong> - provide a fallback response on specific status codes like 429 (rate limited) or 503 (service unavailable)</li>
</ul>

<h4>Configuration</h4>
<ul>
    <li><strong>httpForward</strong> - the upstream target (host, port, scheme)</li>
    <li><strong>fallbackResponse</strong> - the mock response to return when fallback is triggered</li>
    <li><strong>fallbackOnStatusCodes</strong> - list of HTTP status codes that trigger the fallback (defaults to 500-599 when not specified)</li>
    <li><strong>fallbackOnTimeout</strong> - whether to return the fallback on connection errors or timeouts (defaults to true)</li>
</ul>

<h4>Java Client Example</h4>

<pre class="prettyprint lang-java code"><code class="code">new MockServerClient("localhost", 1080)
    .when(
        request()
            .withPath("/api/downstream")
    )
    .forwardWithFallback(
        forwardWithFallback()
            .withForward(
                forward()
                    .withHost("downstream-service.example.com")
                    .withPort(443)
                    .withScheme(HttpForward.Scheme.HTTPS)
            )
            .withFallback(
                response()
                    .withStatusCode(200)
                    .withBody("{\"status\": \"cached\", \"data\": []}")
            )
            .withFallbackOnStatusCodes(500, 502, 503, 504)
            .withFallbackOnTimeout(true)
    );</code></pre>

<h4>JSON Example</h4>

<pre class="prettyprint lang-json code"><code class="code">{
    "httpRequest": {
        "path": "/api/downstream"
    },
    "httpForwardWithFallback": {
        "httpForward": {
            "host": "downstream-service.example.com",
            "port": 443,
            "scheme": "HTTPS"
        },
        "fallbackResponse": {
            "statusCode": 200,
            "body": "{\"status\": \"cached\", \"data\": []}"
        },
        "fallbackOnStatusCodes": [500, 502, 503, 504],
        "fallbackOnTimeout": true
    }
}</code></pre>

<a id="error_action" class="anchor" href="#error_action">&nbsp;</a>

<p>An <strong>error action</strong> can return an invalid response as a <a href="#button_random_bytes_error">sequence of bytes</a> or <a href="#button_drop_connection_error">drop the connection</a> (with an optional <a href="#button_error_with_delay">delay</a>)</p>

<button id="button_error" class="accordion title"><strong>Error Action Code Examples</strong></button>
<div class="panel title">
    {% include_subpage _includes/error_action_code_examples.html %}
</div>

<a id="llm_response_action" class="anchor" href="#llm_response_action">&nbsp;</a>

<h3>LLM Response</h3>

<p>An <strong>LLM response action</strong> (<span class="inline_code">httpLlmResponse</span>) returns a provider-correct response from a high-level description of what the model should say. Instead of hand-assembling Anthropic or OpenAI response JSON, you describe the <em>intent</em> (text, tool calls, usage, stop reason) and MockServer produces the byte-correct wire format.</p>

<p>Seven providers have full codec support: <strong>ANTHROPIC</strong>, <strong>OPENAI</strong> (Chat Completions), <strong>OPENAI_RESPONSES</strong>, <strong>GEMINI</strong>, <strong>BEDROCK</strong>, <strong>AZURE_OPENAI</strong>, and <strong>OLLAMA</strong> — each produces the byte-correct wire format for both single completions and streaming. A request naming an unrecognised provider returns a structured 400 response listing the supported providers.</p>

<h4>Java Client Example</h4>

<pre class="prettyprint lang-java code"><code class="code">import static org.mockserver.client.LlmMockBuilder.llmMock;
import static org.mockserver.model.Completion.completion;
import static org.mockserver.model.Provider.ANTHROPIC;
import static org.mockserver.model.ToolUse.toolUse;
import static org.mockserver.model.Usage.usage;

// Simple text completion
llmMock("/v1/messages")
    .withProvider(ANTHROPIC)
    .withModel("claude-sonnet-4")
    .respondingWith(
        completion()
            .withText("The capital of France is Paris.")
            .withStopReason("end_turn")
            .withUsage(usage().withInputTokens(42).withOutputTokens(8))
    )
    .applyTo(mockServerClient);

// Tool / function call
llmMock("/v1/messages")
    .withProvider(ANTHROPIC)
    .respondingWith(
        completion()
            .withText("Let me check the weather.")
            .withToolCall(toolUse("get_weather").withArguments("{\"city\":\"Paris\"}"))
            .withStopReason("tool_use")
    )
    .applyTo(mockServerClient);</code></pre>

<h4>JSON Definition Format</h4>

<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/v1/messages"
    },
    "httpLlmResponse": {
        "provider": "ANTHROPIC",
        "model": "claude-sonnet-4",
        "completion": {
            "text": "The capital of France is Paris.",
            "stopReason": "end_turn",
            "usage": { "inputTokens": 42, "outputTokens": 8 }
        }
    }
}</code></pre>

<h4>Streaming with Realistic Timing</h4>

<p>When <span class="inline_code">streaming</span> is enabled, MockServer expands the completion into provider-correct streaming events with configurable timing physics. Most providers use SSE (Server-Sent Events with <span class="inline_code">text/event-stream</span>) &mdash; for example, <span class="inline_code">message_start</span> through <span class="inline_code">message_stop</span> for Anthropic, and <span class="inline_code">chat.completion.chunk</span> deltas for OpenAI. Two providers use alternative wire formats: <strong>Ollama</strong> uses native <strong>NDJSON</strong> (newline-delimited JSON with <span class="inline_code">application/x-ndjson</span>), and <strong>Bedrock</strong> uses the AWS <strong>event-stream</strong> binary framing (<span class="inline_code">application/vnd.amazon.eventstream</span>) where each streaming chunk is a binary message containing a base64-wrapped JSON payload &mdash; matching the <span class="inline_code">InvokeModelWithResponseStream</span> wire format. MockServer emits the correct wire format automatically based on the provider.</p>

<pre class="prettyprint lang-java code"><code class="code">import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.mockserver.client.Llm.jitter;
import static org.mockserver.client.Llm.timeToFirstToken;
import static org.mockserver.client.Llm.tokensPerSecond;
import static org.mockserver.client.LlmMockBuilder.llmMock;
import static org.mockserver.model.Completion.completion;
import static org.mockserver.model.Provider.OPENAI;

llmMock("/v1/chat/completions")
    .withProvider(OPENAI)
    .withModel("gpt-4o")
    .respondingWith(
        completion()
            .withText("Streaming token by token...")
            .streaming()
            .withStreamingPhysics(
                timeToFirstToken(300, MILLISECONDS),
                tokensPerSecond(50),
                jitter(0.2))
    )
    .applyTo(mockServerClient);</code></pre>

<h4>Embeddings (Deterministic from Input)</h4>

<p>For OpenAI embeddings, <span class="inline_code">deterministicFromInput()</span> generates reproducible vectors seeded from the input text. Same input + same dimensions + same seed produces an identical L2-normalised vector across JVMs.</p>

<pre class="prettyprint lang-java code"><code class="code">import static org.mockserver.client.Llm.embedding;
import static org.mockserver.client.LlmMockBuilder.llmMock;
import static org.mockserver.model.Provider.OPENAI;

llmMock("/v1/embeddings")
    .withProvider(OPENAI)
    .respondingWith(
        embedding()
            .withDimensions(1536)
            .deterministicFromInput()
    )
    .applyTo(mockServerClient);</code></pre>

<h4>Structured-Output Validation</h4>

<p>Attach an <span class="inline_code">outputSchema</span> (a JSON Schema) to a completion and MockServer validates the response <span class="inline_code">text</span> against it as the response is encoded. Validation is <strong>fail-soft</strong>: a mismatch never changes the response body &mdash; it adds an <span class="inline_code">x-mockserver-structured-output-invalid</span> response header and logs a warning, so a malformed structured-output fixture is surfaced without breaking the test (a blank or invalid schema, or a missing <span class="inline_code">text</span>, is a no-op). To assert schema conformance over already-recorded traffic instead, use the <a href="/mock_server/ai_mcp_tools.html#verify_structured_output"><span class="inline_code">verify_structured_output</span></a> tool.</p>

<pre class="prettyprint lang-java code"><code class="code">import static org.mockserver.client.LlmMockBuilder.llmMock;
import static org.mockserver.model.Completion.completion;
import static org.mockserver.model.Provider.ANTHROPIC;

llmMock("/v1/messages")
    .withProvider(ANTHROPIC)
    .respondingWith(
        completion()
            .withText("{\"city\":\"Paris\",\"country\":\"France\"}")
            .withOutputSchema("{\"type\":\"object\",\"required\":[\"city\",\"country\"]}")
    )
    .applyTo(mockServerClient);</code></pre>

<h4>Fault Injection &amp; Rate-Limit Quota</h4>

<p>A <span class="inline_code">chaos</span> block on the LLM response injects faults for resilience testing: probabilistic provider errors (e.g. 429/529 with a <span class="inline_code">Retry-After</span> header), mid-stream truncation, malformed SSE, and a <strong>stateful request quota</strong>. The quota is a deterministic fixed-window rate limit &mdash; expectations sharing a <span class="inline_code">quotaName</span> share one counter, so requests past <span class="inline_code">quotaLimit</span> within <span class="inline_code">quotaWindowMillis</span> are rejected with <span class="inline_code">quotaErrorStatus</span> (default 429) and the <span class="inline_code">retryAfter</span> header (the count resets when the window elapses and on server reset). The <span class="inline_code">chaos</span> block is set via the JSON definition (below) or the <a href="/mock_server/ai_mcp_tools.html#mock_llm_completion"><span class="inline_code">mock_llm_completion</span></a> MCP tool, where the full field reference lives.</p>

<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "POST",
        "path": "/v1/messages"
    },
    "httpLlmResponse": {
        "provider": "ANTHROPIC",
        "completion": {
            "text": "The capital of France is Paris."
        },
        "chaos": {
            "quotaName": "anthropic-account",
            "quotaLimit": 100,
            "quotaWindowMillis": 60000,
            "quotaErrorStatus": 429,
            "retryAfter": "30"
        }
    }
}</code></pre>

<p>For multi-turn LLM conversations with stateful scenario progression, see the <a href="#llm_conversation_example">LLM Conversation</a> example in the <a href="#stateful_scenarios">Stateful Scenarios</a> section.</p>

<a id="websocket_response_action" class="anchor" href="#websocket_response_action">&nbsp;</a>

<h3>WebSocket Response</h3>

<p>A <strong>WebSocket response action</strong> (<span class="inline_code">httpWebSocketResponse</span>) accepts an HTTP Upgrade request and turns the connection into a WebSocket session. Two modes are available:</p>

<ul>
    <li><strong>Server-push</strong> — a fixed sequence of messages sent to the client immediately after the handshake, defined in the <span class="inline_code">messages</span> array.</li>
    <li><strong>Bidirectional</strong> — incoming WebSocket frames are matched against a list of <span class="inline_code">matchers</span>; the first matching entry triggers its own response sequence. Matchers are evaluated in order; first match wins.</li>
</ul>

<p>Both modes can be combined in a single expectation: MockServer sends any <span class="inline_code">messages</span> immediately after the upgrade, then waits for incoming frames and applies <span class="inline_code">matchers</span> for each one received.</p>

<h4>Bidirectional Frame Matching</h4>

<p>Each entry in the <span class="inline_code">matchers</span> array contains:</p>

<ul>
    <li><strong>frameType</strong> — the WebSocket frame type to match: <span class="inline_code">TEXT</span>, <span class="inline_code">BINARY</span>, <span class="inline_code">PING</span>, <span class="inline_code">PONG</span>, or <span class="inline_code">ANY</span> (matches any type).</li>
    <li><strong>textMatcher</strong> — optional substring or exact string the text frame payload must contain. Omit to match all frames of the given <span class="inline_code">frameType</span>.</li>
    <li><strong>responses</strong> — the sequence of messages to send back when this matcher fires. Each entry has a <span class="inline_code">text</span> field (and an optional <span class="inline_code">delay</span>).</li>
</ul>

<h4>JSON Definition Format</h4>

<pre class="prettyprint lang-java code"><code class="code">{
    "httpRequest": {
        "method": "GET",
        "path": "/ws/chat"
    },
    "httpWebSocketResponse": {
        "messages": [
            {"text": "{\"type\": \"connected\"}"}
        ],
        "matchers": [
            {
                "frameType": "TEXT",
                "textMatcher": "ping",
                "responses": [
                    {"text": "{\"type\": \"pong\"}"}
                ]
            },
            {
                "frameType": "TEXT",
                "textMatcher": "subscribe",
                "responses": [
                    {"text": "{\"type\": \"ack\"}"},
                    {"text": "{\"type\": \"data\", \"value\": 42}"}
                ]
            }
        ],
        "closeConnection": false
    }
}</code></pre>

<p>In this example, MockServer sends <span class="inline_code">{"type": "connected"}</span> immediately after the WebSocket upgrade. If the client then sends a frame containing <span class="inline_code">ping</span>, MockServer replies with <span class="inline_code">{"type": "pong"}</span>. A frame containing <span class="inline_code">subscribe</span> triggers a two-message reply sequence. Frames that match no entry are silently ignored.</p>

<p>Bidirectional WebSocket expectations are also autherable in the dashboard Composer — select <strong>WebSocket response</strong> as the action type and use the <strong>Bidirectional frame matchers</strong> panel to add entries.</p>

<a id="crud_simulation" class="anchor" href="#crud_simulation">&nbsp;</a>

<h3>CRUD Simulation</h3>

<p>CRUD Simulation lets you quickly stand up a fully functional RESTful resource without writing individual expectations for each HTTP method. You provide a <strong>base path</strong> and optional configuration, and MockServer automatically generates five endpoints that manage an in-memory collection of JSON objects.</p>

<p>This is useful when your tests need a realistic data store (e.g. users, products, orders) that supports create, read, update, and delete operations without manually defining expectations for each verb.</p>

<h4>Auto-Generated Endpoints</h4>

<p>Given a <span class="inline_code">basePath</span> of <span class="inline_code">/api/users</span>, MockServer generates:</p>

<table class="table">
    <thead>
        <tr><th>Method</th><th>Path</th><th>Description</th><th>Success Status</th></tr>
    </thead>
    <tbody>
        <tr><td><span class="inline_code">GET</span></td><td><span class="inline_code">/api/users</span></td><td>List all items</td><td>200</td></tr>
        <tr><td><span class="inline_code">POST</span></td><td><span class="inline_code">/api/users</span></td><td>Create a new item</td><td>201</td></tr>
        <tr><td><span class="inline_code">GET</span></td><td><span class="inline_code">/api/users/{id}</span></td><td>Get item by ID</td><td>200</td></tr>
        <tr><td><span class="inline_code">PUT</span></td><td><span class="inline_code">/api/users/{id}</span></td><td>Update item by ID</td><td>200</td></tr>
        <tr><td><span class="inline_code">DELETE</span></td><td><span class="inline_code">/api/users/{id}</span></td><td>Delete item by ID</td><td>204</td></tr>
    </tbody>
</table>

<h4>JSON Definition Format</h4>

<p>A CRUD simulation is defined by a JSON object with the following fields:</p>
<ul>
    <li><strong>basePath</strong> (required) - the URL path prefix for the generated endpoints, e.g. <span class="inline_code">/api/users</span></li>
    <li><strong>idField</strong> (optional, default <span class="inline_code">"id"</span>) - the field name used as the unique identifier in each JSON object</li>
    <li><strong>idStrategy</strong> (optional, default <span class="inline_code">"AUTO_INCREMENT"</span>) - how IDs are generated for new items:
        <ul>
            <li><span class="inline_code">AUTO_INCREMENT</span> - sequential numeric IDs (1, 2, 3, ...)</li>
            <li><span class="inline_code">UUID</span> - random UUID strings</li>
        </ul>
    </li>
    <li><strong>initialData</strong> (optional) - an array of JSON objects to pre-populate the data store</li>
</ul>

<h4>REST API</h4>

<p>Register a CRUD simulation using <span class="inline_code">PUT /mockserver/crud</span>:</p>
<pre class="prettyprint lang-java code"><code class="code">curl -X PUT "http://localhost:1080/mockserver/crud" \
  -H "Content-Type: application/json" \
  -d '{
    "basePath": "/api/users",
    "idField": "id",
    "idStrategy": "AUTO_INCREMENT",
    "initialData": [
      {"name": "Alice", "email": "alice@example.com"},
      {"name": "Bob", "email": "bob@example.com"}
    ]
  }'</code></pre>

<p>After registration, the endpoints are immediately available:</p>
<pre class="prettyprint lang-java code"><code class="code"># List all users
curl http://localhost:1080/api/users

# Create a new user
curl -X POST http://localhost:1080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Charlie", "email": "charlie@example.com"}'

# Get user by ID
curl http://localhost:1080/api/users/1

# Update user
curl -X PUT http://localhost:1080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice Updated", "email": "alice@example.com"}'

# Delete user
curl -X DELETE http://localhost:1080/api/users/3</code></pre>

<h4>Java Client Example</h4>
<pre class="prettyprint lang-java code"><code class="code">new MockServerClient("localhost", 1080)
    .crud(
        new CrudExpectationsDefinition()
            .withBasePath("/api/users")
            .withIdField("id")
            .withIdStrategy(CrudExpectationsDefinition.IdStrategy.AUTO_INCREMENT)
    );</code></pre>

<h4>UUID ID Strategy Example</h4>
<pre class="prettyprint lang-java code"><code class="code">curl -X PUT "http://localhost:1080/mockserver/crud" \
  -H "Content-Type: application/json" \
  -d '{
    "basePath": "/api/products",
    "idStrategy": "UUID"
  }'

# Create a product - ID will be a UUID like "550e8400-e29b-41d4-a716-446655440000"
curl -X POST http://localhost:1080/api/products \
  -H "Content-Type: application/json" \
  -d '{"name": "Widget", "price": 9.99}'</code></pre>

<p>CRUD simulations are independent of regular expectations and are cleared when MockServer is <a href="/mock_server/clearing_and_resetting.html">reset</a>. You can register multiple CRUD simulations with different base paths simultaneously.</p>
