Pact Contract Import, Export & Verification
MockServer supports the complete Pact v3 contract-testing loop: import a contract to generate stub expectations, export active expectations as a new contract, and verify that existing expectations satisfy a contract. Each direction is a separate endpoint.
For an overview of how these endpoints fit into a full contract-testing workflow, including the OpenAPI contract-test endpoint (/mockserver/contractTest), see Contract Testing.
Pact Import Endpoint
Send a PUT request to /mockserver/pact/import with a Pact v3 consumer contract as the request body. MockServer generates one expectation per interaction and returns 201 Created with the generated expectations as JSON.
This is the inverse of the export endpoint below: rather than producing a contract from active expectations, it creates expectations from the contract, turning a published contract into a ready-to-use stub provider.
The same operation is also available at the generic import endpoint with the ?format=pact query parameter, or via auto-detection (a body with a top-level interactions array is auto-detected as Pact). See Importing Expectations for the generic endpoint and redaction options.
How interactions become expectations
For each interaction, the importer builds:
- A request matcher from request.method, request.path, request.query, request.headers, and request.body
- A response from response.status, response.headers, and response.body
JSON request bodies are matched semantically (key-order and whitespace insensitive). The expectation ID is set to the interaction's description field, or pact-<index> when the description is blank. Re-importing the same contract updates existing expectations in place.
Provider states
A Pact interaction's provider state — its providerState (v2) or providerStates (v3) "given ..." precondition — is preserved on import. The generated expectation is gated on a scenario named pact-provider-state whose required state is the provider-state name, so the interaction only matches once that provider state has been activated. Interactions without a provider state are unaffected and always match.
During verification MockServer activates each interaction's provider state before matching it, so a contract that uses provider states verifies end-to-end. Provider states are also round-tripped on export. Only the first provider state on an interaction gates matching (a scenario holds one active state at a time).
matchingRules mapping
Pact v3 matchingRules.request are translated to MockServer matcher values:
| Pact rule | MockServer matcher value |
|---|---|
| regex | The supplied regex pattern |
| include | .*<value>.* (substring regex) |
| type / number / integer / decimal / boolean | .+ (any non-empty value) |
| Unrecognised / no rule | The concrete example value (exact match) |
Sensitive data redaction
The same redaction rules apply as for HAR and Postman imports: credential-bearing headers and common secret body fields are masked by default. Pass ?redactSensitiveData=false to import values verbatim. See Sensitive Data Redaction for the full list of query parameters.
Each language client has a typed pactImport helper (pact_import / PactImport) that wraps PUT /mockserver/pact/import and returns the upserted expectations. Pass the Pact v3 contract as a JSON string. The REST API tab shows the equivalent raw request.
import org.mockserver.client.MockServerClient;
MockServerClient client = new MockServerClient("localhost", 1080);
String generatedExpectations = client.pactImport("{" +
"\"consumer\": {\"name\": \"frontend\"}," +
"\"provider\": {\"name\": \"users-service\"}," +
"\"interactions\": [{" +
" \"description\": \"get users\"," +
" \"request\": {\"method\": \"GET\", \"path\": \"/api/users\"}," +
" \"response\": {\"status\": 200, \"headers\": {\"content-type\": [\"application/json\"]}, \"body\": {\"users\": []}}" +
"}]," +
"\"metadata\": {\"pactSpecification\": {\"version\": \"3.0.0\"}}" +
"}");
// generatedExpectations holds the upserted expectations as a JSON array string
var mockServerClient = require('mockserver-client').mockServerClient;
var pact = {
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
};
mockServerClient("localhost", 1080).pactImport(JSON.stringify(pact)).then(
function (expectations) {
console.log(expectations); // the upserted expectations, one per interaction
},
function (error) {
console.log(error);
}
);
import json
from mockserver.client import MockServerClient
pact = {
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}
client = MockServerClient("localhost", 1080)
generated_expectations = client.pact_import(json.dumps(pact))
# generated_expectations holds the upserted expectations as a JSON array string
require 'mockserver-client'
require 'json'
pact = {
"consumer" => { "name" => "frontend" },
"provider" => { "name" => "users-service" },
"interactions" => [{
"description" => "get users",
"request" => { "method" => "GET", "path" => "/api/users" },
"response" => { "status" => 200, "headers" => { "content-type" => ["application/json"] }, "body" => { "users" => [] } }
}],
"metadata" => { "pactSpecification" => { "version" => "3.0.0" } }
}
client = MockServer::Client.new('localhost', 1080)
generated_expectations = client.pact_import(pact.to_json)
# generated_expectations holds the upserted expectations as a JSON array string
import mockserver "github.com/mock-server/mockserver-monorepo/mockserver-client-go/v7"
client := mockserver.New("localhost", 1080)
pactJSON := `{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}`
expectations, _ := client.PactImport(pactJSON)
// expectations holds the upserted expectations, one per interaction
using MockServer.Client;
using var client = new MockServerClient("localhost", 1080);
var pactJson = @"{
""consumer"": {""name"": ""frontend""},
""provider"": {""name"": ""users-service""},
""interactions"": [{
""description"": ""get users"",
""request"": {""method"": ""GET"", ""path"": ""/api/users""},
""response"": {""status"": 200, ""headers"": {""content-type"": [""application/json""]}, ""body"": {""users"": []}}
}],
""metadata"": {""pactSpecification"": {""version"": ""3.0.0""}}
}";
var expectations = client.PactImport(pactJson);
// expectations holds the upserted expectations, one per interaction
use mockserver_client::ClientBuilder;
let client = ClientBuilder::new("localhost", 1080).build().unwrap();
let pact_json = r#"{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}"#;
let expectations = client.pact_import(pact_json).unwrap();
// expectations holds the upserted expectations, one per interaction
use MockServer\MockServerClient;
$pactJson = <<<'JSON'
{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}
JSON;
$client = new MockServerClient('localhost', 1080);
$generatedExpectations = $client->pactImport($pactJson);
// $generatedExpectations holds the upserted expectations as a JSON array string
curl -v -X PUT "http://localhost:1080/mockserver/pact/import" \
-H "Content-Type: application/json" \
-d '{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "headers": {"content-type": ["application/json"]}, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}'
MockServer returns 201 Created with the generated expectations as JSON:
[ {
"id" : "get users",
"httpRequest" : {
"method" : "GET",
"path" : "/api/users"
},
"httpResponse" : {
"statusCode" : 200,
"headers" : {
"content-type" : [ "application/json" ]
},
"body" : "{\"users\":[]}"
}
} ]
Pact Export Endpoint
Send a PUT request to /mockserver/pact to export the currently active response expectations as a Pact v3 consumer contract JSON document.
| Query parameter | Required | Default | Description |
|---|---|---|---|
| consumer | No | consumer | The consumer name written into the Pact contract's consumer.name field. |
| provider | No | provider | The provider name written into the Pact contract's provider.name field. |
MockServer responds with HTTP 200 and the Pact contract as pretty-printed JSON.
What Gets Exported
Only expectations that have a concrete HTTP request matcher and a response action are exported. Expectations using forward, callback, or template actions have no direct Pact equivalent and are skipped.
- Expectations whose method or path is a notted matcher (match anything except) are skipped — there is no positive Pact equivalent.
- Notted header or query-parameter values are dropped from the exported interaction; non-notted values are included.
- JSON bodies are embedded as structured JSON in the Pact interaction rather than as escaped strings.
- The interaction description is the expectation ID when a meaningful (non-UUID) ID was set, otherwise METHOD path (e.g. GET /api/users).
- The exported contract uses Pact specification version 3.0.0. Matcher values are emitted as concrete example values (Pact's default exact matching); MockServer matching rules (regex, JSON-schema, XPath) are not translated into Pact matchingRules.
Example
Given an active expectation that mocks GET /api/users, export a Pact contract for the frontend consumer and users-service provider:
Each language client has a typed pactExport(consumer, provider) helper (pact_export / PactExport) that wraps PUT /mockserver/pact and returns the generated Pact v3 contract. The REST API tab shows the equivalent raw request.
import org.mockserver.client.MockServerClient;
MockServerClient client = new MockServerClient("localhost", 1080);
String pactContract = client.pactExport("frontend", "users-service");
// pactContract holds the Pact v3 contract as JSON
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).pactExport("frontend", "users-service").then(
function (pactContract) {
console.log(pactContract); // the Pact v3 contract as JSON
},
function (error) {
console.log(error);
}
);
from mockserver.client import MockServerClient
client = MockServerClient("localhost", 1080)
pact_contract = client.pact_export("frontend", "users-service")
# pact_contract holds the Pact v3 contract as JSON
require 'mockserver-client'
client = MockServer::Client.new('localhost', 1080)
pact_contract = client.pact_export(consumer: 'frontend', provider: 'users-service')
# pact_contract holds the Pact v3 contract as JSON
import mockserver "github.com/mock-server/mockserver-monorepo/mockserver-client-go/v7"
client := mockserver.New("localhost", 1080)
pactContract, _ := client.PactExport("frontend", "users-service")
// pactContract holds the Pact v3 contract as JSON
using MockServer.Client;
using var client = new MockServerClient("localhost", 1080);
var pactContract = client.PactExport("frontend", "users-service");
// pactContract holds the Pact v3 contract as JSON
use mockserver_client::ClientBuilder;
let client = ClientBuilder::new("localhost", 1080).build().unwrap();
let pact_contract = client.pact_export("frontend", "users-service").unwrap();
// pact_contract holds the Pact v3 contract as JSON
use MockServer\MockServerClient;
$client = new MockServerClient('localhost', 1080);
$pactContract = $client->pactExport('frontend', 'users-service');
// $pactContract holds the Pact v3 contract as JSON
curl -v -X PUT "http://localhost:1080/mockserver/pact?consumer=frontend&provider=users-service"
MockServer returns HTTP 200 with the contract as JSON:
{
"consumer" : {
"name" : "frontend"
},
"provider" : {
"name" : "users-service"
},
"interactions" : [ {
"description" : "GET /api/users",
"request" : {
"method" : "GET",
"path" : "/api/users"
},
"response" : {
"status" : 200,
"headers" : {
"content-type" : [ "application/json" ]
},
"body" : {
"users" : [ ]
}
}
} ],
"metadata" : {
"pactSpecification" : {
"version" : "3.0.0"
}
}
}
Consumer-Driven Contract Testing Workflow
A typical workflow using MockServer with Pact:
- Mock the provider — create expectations in MockServer for each API endpoint your consumer calls. Use expectation IDs that are meaningful to use as Pact interaction descriptions.
- Run consumer tests — run your consumer's test suite against MockServer. Tests pass because MockServer returns the expected responses.
- Export the contract — call PUT /mockserver/pact?consumer=my-consumer&provider=my-provider to get the Pact JSON.
- Publish to Pact Broker — publish the exported JSON to your Pact Broker or PactFlow instance so the provider team can verify against it.
Recordings made through MockServer's proxy/spy mode (see Operating Mode) can also be exported: after recording real traffic through a SPY or CAPTURE session, the captured response expectations appear as active expectations and can be exported directly.
Verifying a Pact Contract
Send a PUT request to /mockserver/pact/verify with a Pact v3 contract as the request body. MockServer verifies that its currently-active expectations satisfy every interaction in the contract.
For each interaction, MockServer:
- Builds an HTTP request from the interaction's request fields (method, path, query, headers, body).
- Finds the first active expectation whose request matcher matches this request.
- Compares the matched expectation's response against the interaction's expected response:
- Status code must be equal.
- Headers use subset matching — each header in the Pact response must be present in MockServer's response, but extra headers are allowed.
- Body — if both bodies parse as JSON, they are compared structurally; otherwise string equality is used.
Response Codes
| HTTP Status | Meaning |
|---|---|
| 202 Accepted | All interactions verified successfully. The response body is a JSON summary with "verified": true. |
| 406 Not Acceptable | One or more interactions failed verification. The response body is a JSON summary with "verified": false and per-interaction failure reasons. |
| 400 Bad Request | The request body is empty, not valid JSON, or contains no interactions. |
Result Format
The JSON response body contains an overall verified flag and a per-interaction breakdown:
{
"verified" : false,
"interactions" : [ {
"description" : "get users",
"verified" : true
}, {
"description" : "create user",
"verified" : false,
"reason" : "status code mismatch: expected 201 but was 200"
} ]
}
Limitations
- Only expectations with a static response action (single response or response sequence) can be verified. Expectations using forward, callback, or template actions will fail verification with the reason "unverifiable (non-static action)".
- For response sequences, verification compares against the first response in the sequence.
Each language client has a typed pactVerify helper (pact_verify / PactVerify) that wraps PUT /mockserver/pact/verify. The server replies 202 Accepted when every interaction verifies and 406 Not Acceptable otherwise — a FAIL is a normal outcome and does not raise. The return type reflects how each language exposes that pass/fail verdict: Java, Python, and Ruby return the verification report string (inspect its verified flag); Node returns the parsed report object; Go returns (passed bool, report string, err error); Rust returns a PactVerification with passed and report fields; and .NET and PHP return a bool. The REST API tab shows the equivalent raw request.
import org.mockserver.client.MockServerClient;
MockServerClient client = new MockServerClient("localhost", 1080);
String verificationReport = client.pactVerify("{" +
"\"consumer\": {\"name\": \"frontend\"}," +
"\"provider\": {\"name\": \"users-service\"}," +
"\"interactions\": [{" +
" \"description\": \"get users\"," +
" \"request\": {\"method\": \"GET\", \"path\": \"/api/users\"}," +
" \"response\": {\"status\": 200, \"body\": {\"users\": []}}" +
"}]," +
"\"metadata\": {\"pactSpecification\": {\"version\": \"3.0.0\"}}" +
"}");
// verificationReport holds the per-interaction verification result as JSON
var mockServerClient = require('mockserver-client').mockServerClient;
var pact = {
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
};
mockServerClient("localhost", 1080).pactVerify(JSON.stringify(pact)).then(
function (report) {
console.log("verified: " + report.verified); // report carries the per-interaction breakdown
},
function (error) {
console.log(error);
}
);
import json
from mockserver.client import MockServerClient
pact = {
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}
client = MockServerClient("localhost", 1080)
report = client.pact_verify(json.dumps(pact)) # the verification report JSON string
verified = json.loads(report)["verified"]
require 'mockserver-client'
require 'json'
pact = {
"consumer" => { "name" => "frontend" },
"provider" => { "name" => "users-service" },
"interactions" => [{
"description" => "get users",
"request" => { "method" => "GET", "path" => "/api/users" },
"response" => { "status" => 200, "body" => { "users" => [] } }
}],
"metadata" => { "pactSpecification" => { "version" => "3.0.0" } }
}
client = MockServer::Client.new('localhost', 1080)
report = client.pact_verify(pact.to_json) # the verification report JSON string
verified = JSON.parse(report)['verified']
import mockserver "github.com/mock-server/mockserver-monorepo/mockserver-client-go/v7"
client := mockserver.New("localhost", 1080)
pactJSON := `{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}`
passed, report, err := client.PactVerify(pactJSON)
// passed == true when every interaction verified (202); report holds the report JSON
_ = report
_ = err
using MockServer.Client;
using var client = new MockServerClient("localhost", 1080);
var json = @"{
""consumer"": {""name"": ""frontend""},
""provider"": {""name"": ""users-service""},
""interactions"": [{
""description"": ""get users"",
""request"": {""method"": ""GET"", ""path"": ""/api/users""},
""response"": {""status"": 200, ""body"": {""users"": []}}
}],
""metadata"": {""pactSpecification"": {""version"": ""3.0.0""}}
}";
bool verified = client.PactVerify(json);
// verified == true when every interaction verified (202), false otherwise (406)
use mockserver_client::ClientBuilder;
let client = ClientBuilder::new("localhost", 1080).build().unwrap();
let pact_json = r#"{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}"#;
let verification = client.pact_verify(pact_json).unwrap();
// verification.passed == true on 202, false on 406; verification.report holds the report JSON
use MockServer\MockServerClient;
$pactJson = <<<'JSON'
{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}
JSON;
$client = new MockServerClient('localhost', 1080);
$verified = $client->pactVerify($pactJson);
// $verified === true when every interaction verified (202), false otherwise (406)
curl -v -X PUT "http://localhost:1080/mockserver/pact/verify" \
-H "Content-Type: application/json" \
-d '{
"consumer": {"name": "frontend"},
"provider": {"name": "users-service"},
"interactions": [{
"description": "get users",
"request": {"method": "GET", "path": "/api/users"},
"response": {"status": 200, "body": {"users": []}}
}],
"metadata": {"pactSpecification": {"version": "3.0.0"}}
}'
If all interactions verify, MockServer returns HTTP 202:
{
"verified" : true,
"interactions" : [ {
"description" : "get users",
"verified" : true
} ]
}
Full Consumer-Driven Contract Loop
With import, export, and verify, MockServer supports the complete consumer-driven contract testing loop:
- Consumer side — create expectations in MockServer and run consumer tests against them.
- Export — call PUT /mockserver/pact to export the contract.
- Provider side — import the contract into a separate MockServer instance with PUT /mockserver/pact/import to stub the provider, or call PUT /mockserver/pact/verify to verify that existing provider expectations satisfy the contract.
- Publish — optionally publish the contract to a Pact Broker for cross-team visibility.