Smart mock creates a fake API in seconds. It reads your endpoint schema and returns plausible data: realistic emails, sensible timestamps, and usable names. For most frontend work, that is enough to unblock development.
But some cases require request-aware behavior. You may want /login to return 200 for a known user and 401 for everyone else. Or /orders/{id} to return a shipped order for one ID and a cancelled order for another. You may also need to force a 500 so error handling is tested before production.
Smart mock returns one generated response shape per endpoint, so it cannot branch on the incoming request. This guide shows how to fill that gap with:
- Mock expectations for rule-based conditional responses.
- Mock scripts for logic that fixed rules cannot express.
For background, see the API mocking overview. This tutorial uses Apidog and follows the contract-first workflow described by the OpenAPI Initiative.
What conditional mocking means
A conditional mock is a rule:
When the request looks like this, return that response.
Apidog provides two layers for mocking.
Field-level customization inside an endpoint schema
Use fixed values or dynamic Faker.js expressions. This controls individual field values, but the endpoint still has one response shape.Full-response mock expectations
An expectation is a named rule with optional conditions, a response body, status code, headers, and delay.
An expectation with no conditions always returns its configured response. An expectation with conditions responds only when the request matches. By combining expectations, you can return:
- A different body for each path parameter.
- An error when a required header is absent.
- A specific status code for a known request body.
- A fallback response for every other request.
Field-level dynamic values first
Before adding request-based branches, configure dynamic values for a single response shape.
In an endpoint schema, string fields can use Faker.js expressions in the format {{$category.method}}. Apidog resolves these expressions on every mock call using the types declared in your JSON Schema.
{
"id": "{{$number.int(min=1000,max=9999)}}",
"customer": "{{$person.fullName}}",
"email": "{{$internet.email}}",
"product": "{{$commerce.productName}}",
"shippingAddress": "{{$location.streetAddress}}, {{$location.city}}",
"orderedAt": "{{$date.between(from='2024-01-01',to='2024-12-31',format='yyyy-MM-dd')}}"
}
Parameterized methods let you constrain generated values:
-
{{$number.int(min=1000,max=9999)}}generates IDs in a known range. -
{{$date.between(...)}}constrains dates and output format. - You can combine static text and multiple expressions in one field.
Apidog also supports configurable mock locales for region-specific names, addresses, and phone numbers. See the Faker.js reference in Apidog for available methods.
This is Smart mock territory: values are dynamic, but the response is not conditional. To branch based on the request, use expectations.
Walkthrough: return 200 or 401 from POST /login
Assume POST /login accepts this JSON body:
{
"username": "alice@example.com",
"password": "whatever"
}
The desired behavior is:
-
alice@example.comreceives200and a token. - Every other user receives
401.
1. Open the expectation list
The UI location depends on your working mode:
- In DEBUG (Request-first) mode, open the endpoint and select the Mock tab.
- In DESIGN (Design-first) mode, open the endpoint and select the Advanced mock tab.
Both views open the same expectation list. If needed, download Apidog and create or import a /login endpoint first.
2. Add the success expectation
Click New expectation and configure the following:
-
Expectation name:
login-success - Condition type: body parameter
-
Name:
username - Condition: equals
-
Value:
alice@example.com
For JSON request bodies, body conditions use the JSON path entered in the name field. For nested properties, use dot notation such as user.email.
Set Response data to:
{
"token": "mock-jwt-{{$string.uuid}}",
"user": {
"id": 4821,
"username": "alice@example.com",
"role": "member"
}
}
Save the expectation. The default HTTP status is 200, so no additional status configuration is required.
3. Add the catch-all failure expectation
Create another expectation:
-
Expectation name:
login-failure - Conditions: none
Leaving conditions blank makes this rule a catch-all. Set Response data to:
{
"error": "invalid_credentials",
"message": "Username or password is incorrect."
}
Open the expectation’s More tab and set:
-
HTTP Status Code:
401
The More tab also lets you configure:
-
Response Delay in milliseconds, defaulting to
0. - Custom response headers.
For example, set a 400 ms delay to verify that your loading UI appears during login.
4. Put expectations in the correct order
Expectations run from top to bottom, and the first matching rule wins.
Order the rules like this:
login-successlogin-failure
The success rule must be first. If the blank-condition failure rule comes first, it matches every request and the success rule never runs.
Test both responses using the endpoint’s mock URL:
# Known user -> 200 with a token
curl -X POST https://<your-mock-host>/login \
-H "Content-Type: application/json" \
-d '{"username":"alice@example.com","password":"whatever"}'
# Any other user -> 401
curl -X POST https://<your-mock-host>/login \
-H "Content-Type: application/json" \
-d '{"username":"stranger@example.com","password":"whatever"}'
Walkthrough: return different /orders/{id} bodies
Path parameters are another common conditional-mocking use case.
For example, configure /orders/{id} so that:
-
/orders/5001returns a shipped order. -
/orders/5002returns a cancelled order. - Any other ID returns a generic pending order.
Create one expectation for each state.
Shipped order
Create an expectation named order-shipped:
- Condition type: path parameter
-
Parameter:
id - Condition: equals
-
Value:
5001
Use this response body:
{
"id": 5001,
"status": "shipped",
"total": 129.9,
"trackingNumber": "1Z{{$string.alphanumeric(length=16)}}",
"shippedAt": "{{$date.recent(days=3,format='yyyy-MM-dd')}}"
}
Cancelled order
Create an expectation named order-cancelled:
- Condition type: path parameter
-
Parameter:
id - Condition: equals
-
Value:
5002
Use this response body:
{
"id": 5002,
"status": "cancelled",
"total": 0,
"cancelledAt": "{{$date.recent(days=1,format='yyyy-MM-dd')}}",
"refundIssued": true
}
Add a fallback
Add one final expectation with no conditions. Return a generic pending order so unknown IDs still get a valid response.
Place the rules in this order:
order-shippedorder-cancelled- Generic pending-order fallback
You can combine condition types. For example, add both a path parameter condition and a header condition. Apidog combines multiple conditions with AND logic, so every configured condition must match.
Conditions can target:
- Request body parameters
- Path parameters
- Query parameters
- Headers
- Cookies
- IP addresses
Force error states on demand
You do not need a failing backend to test frontend error handling.
To force a 500 response, add an expectation that matches a client-controlled header:
-
Header:
X-Mock-Scenario - Condition: equals
-
Value:
server-error -
HTTP Status Code:
500
Set the response body to:
{
"error": "internal_error",
"requestId": "{{$string.uuid}}",
"message": "Something went wrong on our end. Please retry."
}
Now the endpoint can return its usual 200 response by default and return 500 only when the client sends:
X-Mock-Scenario: server-error
Use the same pattern for other states:
-
404for missing resources -
429with aRetry-Afterresponse header -
503for temporary unavailability
If you validate these cases in tests, pair this approach with API assertions.
In shared projects, each expectation can be independently enabled or disabled for local and cloud mock environments. For example, keep a forced 500 rule enabled locally but disabled in the cloud mock used by teammates.
When expectations are not enough: mock scripts
Expectations are declarative: they match requests and return predefined responses. They do not compute values.
Use a mock script when the response must be derived from the incoming request, such as:
- Summing order line items.
- Calculating tax.
- Changing response shape based on several inputs.
- Echoing request headers into the response.
Mock scripts are JavaScript and run in the Mock Script section at the bottom of the Mock tab.
The runtime exposes two globals:
-
$$.mockRequestfor reading the incoming request.getParam(key)headerscookiesbodyformdataurlencoded
-
$$.mockResponsefor changing the outgoing response.setBody()setCode()setDelay()json()headerscode
For example, this script calculates an order total from posted line items and returns the caller’s currency header:
const body = $$.mockRequest.body;
const items = body.items || [];
const subtotal = items.reduce((sum, item) => {
return sum + item.price * item.quantity;
}, 0);
const currency = $$.mockRequest.headers["x-currency"] || "USD";
$$.mockResponse.setCode(201);
$$.mockResponse.setBody({
orderId: Math.floor(Math.random() * 90000) + 10000,
currency: currency,
subtotal: subtotal,
tax: Number((subtotal * 0.08).toFixed(2)),
total: Number((subtotal * 1.08).toFixed(2))
});
The execution flow is:
- Smart mock generates an initial response.
- The script reads
$$.mockRequestand the current$$.mockResponse. - The script applies custom logic.
- The script calls
setBody(),setCode(),setDelay(), or modifies headers. - Apidog returns the final response.
Use the MDN JavaScript reference when you need more complex array, date, or object logic.
Important: scripts do not run with expectations
Mock scripts work only with Smart mock.
They do not apply to:
- Mock expectations
- Response examples
If an expectation matches a request, the mock script does not run.
Choose one approach per endpoint:
- Use expectations for fixed, rule-based branching.
- Use mock scripts for computed output built on Smart mock.
How mock priority works
For every mock request, Apidog resolves the response in this order:
Evaluate expectations from top to bottom
The first expectation whose conditions all match wins. Its response is returned immediately.Fall back to Mock method priority
If no expectation matches, Apidog uses the priority configured in:
Project Settings → Feature Settings → Mock Settings
This is where Smart mock, including any attached mock script, generates the response.
Use this mental model:
Specific rules first, generated data second.
Put the most specific expectations first, place a blank-condition fallback at the bottom when you need a guaranteed response, and let Smart mock handle the remaining cases.
For more examples, see the API mocking use cases guide.
Gotchas to check before shipping
Keep these constraints in mind:
- Parameter conditions do not support
{{variables}}. Apidog project and environment variables are unavailable in mock expectations, so use literal values. - Body-parameter conditions support JSON only. Use JSON paths in the condition name field.
- The request body format in the condition must match the API specification. For a
form-dataendpoint, configure a form-data condition rather than a JSON condition. - Mock scripts have no logging function.
- The
pmobject is unavailable in mock scripts because they run in a different environment from test scripts. - Apidog variables cannot be used inside mock scripts, so keep script logic self-contained.
The docs do not describe plan gating for these features. The local-versus-cloud difference is functional: expectations can be toggled independently in each environment.
Automate the workflow with the Apidog CLI
Mocking itself is a GUI and cloud capability. The mock engine serves endpoints through local and cloud mock URLs; there is no CLI command to start a running mock server.
The Apidog CLI helps manage the API resources that mocks are generated from.
Because mock responses come from endpoint schemas, mock accuracy depends on spec accuracy. The CLI and AI coding agents that use it—such as Cursor, Claude Code, Trae, and Codex—can create and update endpoints and schemas in a project.
A practical workflow looks like this:
- Update the API contract in code.
- Sync the contract to the project.
- Let the mock output reflect the updated schema.
- Run test scenarios in CI against the real backend.
Run a scenario headlessly with:
apidog run -t <scenario_id> -e <env_id> -r cli
This executes test scenarios and reports results, letting the mock contract and backend verification use the same source of truth.
See the Apidog CLI installation guide for setup and the Apidog CLI in GitHub Actions guide for CI integration.
FAQ
Why is my expectation ignored even though the condition looks correct?
Check ordering and request format. Expectations run top to bottom, so a broad blank-condition expectation above a specific one will match first. Also verify that the condition uses the request format defined in the API spec: JSON paths for JSON bodies and form-data placement for form endpoints. Review the API mocking overview if needed.
Can I use a mock script and a mock expectation for the same response?
No. Mock scripts only run with Smart mock. They do not run for mock expectations or response examples. Use expectations for rule-based branches and scripts for computed responses.
How do I return 401 or 500 without breaking the default 200 response?
Create a dedicated expectation with a condition controlled by the client, such as a header. In the expectation’s More tab, set the HTTP status code. The default response remains 200, and the error response is returned only when the condition matches.
Can conditions use environment variables?
No. Apidog {{variable}} values are unavailable in mock expectations, and parameter conditions do not support {{variables}}. Use literal condition values.
What happens when no expectation matches?
Apidog falls back to the Mock method priority configured under Project Settings → Feature Settings → Mock Settings. Smart mock then generates a response from the schema. Add a blank-condition expectation when you need a specific fallback response instead.
Wrapping up
Smart mock handles generated schema-based data. Mock expectations handle request-aware behavior:
-
200for a known user and401for everyone else. - Different order bodies for different IDs.
- A forced
500for error-state testing.
Use mock scripts only when you need computed output that declarative rules cannot express. Remember the priority order: matching expectations run first, and Smart mock is the fallback.
Download Apidog to build your first conditional mock.




Top comments (0)