How a request finds its rule, the condition syntax, stateful scenarios and response templates.
When multiple rules match the same request, the system selects based on the following order:
- matchKey specificity – Exact paths take priority over wildcard
* - priority field – Higher number = higher priority (default 0)
- targetHost specificity (HTTP) – Specified host takes priority over empty value
- Creation time – Newer rules take priority
- Has conditions and matches → Return immediately
- No conditions → Record as fallback, take the first one after sorting
- Finally return the fallback rule or null
| Rule | targetHost | matchKey | priority | Condition | Order |
|---|---|---|---|---|---|
| A | api.com | /users | 10 | type=vip | 1 |
| B | api.com | /users | 10 | (none) | 2 |
| C | (empty) | /users | 10 | (none) | 3 |
| D | api.com | * | 10 | (none) | 4 |
| E | api.com | /users | 1 | (none) | 5 |
- Request body contains
type=vip→ Matches A - Request body does not contain
type=vip→ Matches B
Rules with disabled tags do not participate in matching.
Tags are used to organize rules by version, feature, or environment:
- JSON format – e.g.,
{"env":"prod","team":"payment"} - Batch control – Enable/disable rules by tag (
PUT /api/admin/rules/tag/{key}/{value}/enable|disable) - Quick filter – Filter rules by tag on the rules page, with group view toggle
{
"protocol": "HTTP",
"targetHost": "api.example.com",
"matchKey": "/users",
"method": "GET",
"bodyCondition": "custId=K123",
"queryCondition": "status=active",
"responseBody": "{\"name\": \"VIP User\"}",
"status": 200,
"delayMs": 100
}{
"protocol": "JMS",
"matchKey": "ORDER.QUEUE",
"bodyCondition": "//OrderType=VIP",
"responseBody": "<response><status>OK</status></response>",
"delayMs": 50
}For JMS rules, matchKey is the queue name. status is the HTTP status code of the reply.
| Syntax | Description | Example |
|---|---|---|
field=value |
Simple field | userId=123 |
a.b.c=value |
Nested field | order.customer.id=VIP001 |
arr[0].field=value |
Array index | items[0].sku=A001 |
| Syntax | Description | Matches |
|---|---|---|
status=active |
Query parameter | ?status=active |
id=123 |
Query parameter | ?id=123&other=x |
| Syntax | Description |
|---|---|
element=value |
Simple element (auto-converts to //element) |
//CustomerId=K123 |
XPath anywhere |
/root/order/id=123 |
XPath absolute path |
order/id=123 |
Relative path: any id whose parent is order |
//item/@sku=A1 |
Attribute value |
Simple paths like these match element and attribute local names, so CustomerId also matches <ns:CustomerId>; == is treated as =. The same rules apply to HTTP and JMS and to xpath selectors in advanced conditions, and XML bodies whose conditions only use simple paths are scanned once without building a DOM. Paths with predicates, functions, wildcards or prefixed names (for example //item[1], //ns:item) use XPath.
Multiple conditions separated by ; must all match (AND logic):
- Body:
custId=K123;type=vip - Query:
status=active;page=1 - Header:
X-Api-Key=abc123;Content-Type*=json
| Syntax | Description | Example |
|---|---|---|
Header=value |
Exact match (case-insensitive) | X-Api-Key=abc123 |
Header!=value |
Not equal | Accept!=text/xml |
Header*=value |
Contains | Content-Type*=json |
Header~=regex |
Regex match | Authorization~=Bearer.* |
When ;-joined AND conditions are not enough, a rule can carry a structured condition tree in
matchSpec (REST API, JSON import/export, or echo.mock/v2 documents as spec.match). It supports
all / any / not groups and these operators: equals, notEquals, contains, matches,
exists, absent, gt / gte / lt / lte (numeric), in, and equalToJson.
{
"protocol": "HTTP", "matchKey": "/orders", "method": "POST", "responseBody": "{\"tier\":\"gold\"}",
"matchSpec": {"all": [
{"source": "header", "name": "X-Tenant", "op": "equals", "value": "acme"},
{"any": [
{"source": "body", "selector": {"jsonPath": "$.amount"}, "op": "gte", "value": 1000},
{"source": "query", "name": "vip", "op": "exists"}
]},
{"not": {"source": "body", "selector": {"jsonPath": "$.status"}, "op": "equals", "value": "CANCELLED"}}
]}
}- Sources: HTTP rules use
body,queryandheader; JMS rules usebody(selectorsjsonPath,xpath,field,raw). - Invalid trees are rejected on save with the JSON path of the problem (for example
$.all[1].any[0].op). Limits: 64 KiB, 16 levels, 256 nodes, 1,000invalues. matchSpecandbodyCondition/queryCondition/headerConditionare mutually exclusive. A tree that is only anallof{"legacy": {"source": ..., "condition": ...}}fragments is stored as the legacy fields.- In the rule editor, switch the condition block from Simple to Advanced to build groups, NOT and the new operators; existing simple conditions become legacy fragments that keep their exact matching. Switching back is instant when only legacy fragments remain, otherwise the editor asks before clearing the advanced conditions. The editor checks the tree against the server while you type and points at the failing node.
- A request that omits both
matchSpecand legacy conditions keeps a rule's structured conditions (so older clients cannot erase them by accident); send{"all": []}to clear them.
Rules can participate in a named state machine using scenarioName, match only in
requiredScenarioState, and transition to newScenarioState after a successful match.
Every scenario starts in Started.
{
"protocol": "HTTP",
"matchKey": "/orders/123/pay",
"method": "POST",
"scenarioName": "order-flow",
"requiredScenarioState": "Started",
"newScenarioState": "Paid",
"responseBody": "{\"result\":\"payment-ok\"}"
}Reset one scenario with PUT /api/admin/scenarios/{name}/reset, or all scenarios
with PUT /api/admin/scenarios/reset.
WireMock-style Handlebars template engine. Use {{...}} syntax in response content to generate dynamic content.
Comparison operators: eq, ne, gt, lt, contains, matches
Templates work the same way for every protocol: HTTP responses, SSE events and JMS replies. Content without {{ is returned unchanged at no extra cost.
| Variable | HTTP | JMS |
|---|---|---|
request.body |
Request body | Message text |
request.headers.xxx |
Request header | JMSMessageID, JMSCorrelationID, JMSType, JMSDestination (the listening queue) and the message's own properties |
request.path |
Request path | Queue, plus the endpoint field value when present (ECHO.REQUEST | Order) |
request.method, request.query.xxx |
Method, query parameter | Empty |