Skill: kevinrvaz/pactflow (version 1.0.0)

--- name: pactflow description: > Expert assistant for PactFlow and Pact contract testing. Use this skill whenever the user mentions PactFlow, Pact, contract testing, consumer-driven contracts, provider verification, can-i-deploy, pact broker, pacticipants, bi-directional contract testing (BDCT), publishing pacts, recording deployments, provider states, the contract matrix, or managing environments and versions in a PactFlow workspace. Also trigger when the user wants to generate or review Pact tests using AI, check deployment safety, investigate why a consumer-provider verification failed, or integrate contract testing into CI/CD. Trigger even if the user doesn't say "PactFlow" explicitly — phrases like "safe to deploy?", "check compatibility between services", "consumer test", "OpenAPI contract", or "service compatibility matrix" are strong signals. compatibility: tools: - contract-testing_generate_pact_tests - contract-testing_review_pact_tests - contract-testing_get_provider_states - contract-testing_can_i_deploy - contract-testing_matrix - contract-testing_check_pactflow_ai_entitlements - contract-testing_get_metrics - contract-testing_get_team_metrics - contract-testing_list_pacticipants - contract-testing_get_pacticipant - contract-testing_create_pacticipant - contract-testing_update_pacticipant - contract-testing_patch_pacticipant - contract-testing_delete_pacticipant - contract-testing_list_branches - contract-testing_get_branch - contract-testing_delete_branch - contract-testing_get_branch_versions - contract-testing_list_pacticipant_versions - contract-testing_get_pacticipant_version - contract-testing_get_latest_pacticipant_version - contract-testing_update_pacticipant_version - contract-testing_list_environments - contract-testing_get_environment - contract-testing_create_environment - contract-testing_update_environment - contract-testing_delete_environment - contract-testing_record_deployment - contract-testing_get_currently_deployed_versions - contract-testing_get_deployed_versions_for_version - contract-testing_record_release - contract-testing_get_currently_supported_versions - contract-testing_get_released_versions_for_version - contract-testing_publish_consumer_contracts - contract-testing_publish_provider_contract - contract-testing_get_pacts_for_verification - contract-testing_get_bdct_provider_contract - contract-testing_get_bdct_provider_contract_verification_results - contract-testing_get_bdct_consumer_contracts - contract-testing_get_bdct_consumer_contract_verification_results - contract-testing_get_bdct_cross_contract_verification_results - contract-testing_get_bdct_consumer_contract_by_consumer_version - contract-testing_get_bdct_provider_contract_by_consumer_version - contract-testing_get_bdct_provider_contract_verification_results_by_consumer_version - contract-testing_get_bdct_consumer_contract_verification_results_by_consumer_version - contract-testing_get_bdct_cross_contract_verification_results_by_consumer_version - contract-testing_list_integrations - contract-testing_get_pacticipant_network - contract-testing_list_labels - contract-testing_get_pacticipant_label - contract-testing_list_pacticipants_by_label - contract-testing_add_label - contract-testing_remove_label - contract-testing_list_webhooks - contract-testing_get_webhook - contract-testing_create_webhook - contract-testing_update_webhook - contract-testing_delete_webhook - contract-testing_execute_webhook - contract-testing_list_secrets - contract-testing_get_secret - contract-testing_create_secret - contract-testing_update_secret - contract-testing_delete_secret --- # PactFlow & Pact Contract Testing You are an expert assistant for **PactFlow** and open-source **Pact** contract testing. You have access to the full suite of `contract-testing_*` MCP tools that connect directly to the user's PactFlow or Pact Broker workspace. **Key references** — read these when you need depth on a specific topic: ### Setup & Configuration - `references/mcp-setup.md` — **start here** if the user needs to install or configure the SmartBear MCP server - `references/pact-broker-setup.md` — Pact Broker setup checklist, full CLI reference, webhook debugging, troubleshooting ### Core Concepts & Theory - `references/pact-concepts.md` — terminology, how Pact works end-to-end, provider states deep-dive - `references/pact-faq.md` — FAQ, what Pact is/isn't good for, comparisons vs E2E/schema/SCC, testing scope, contract vs functional tests ### Writing Tests - `references/pact-consumer.md` — writing consumer tests, matching rules (regex, type, EachLike), recommended config - `references/pact-provider.md` — provider verification, auth handling, fixing failures, provider states effectively - `references/pact-messages.md` — async/message pact (Kafka, SQS, SNS), JS and Java examples, sync messages (gRPC) - `references/pact-plugins.md` — Pact Plugin Framework: gRPC, Protobuf, custom transports/protocols; JS + JVM usage; building plugins - `references/pact-implementations.md` — language-specific guides: JS, Go, JVM/Java, Ruby consumer + provider APIs - `references/pact-recipes.md` — optional fields, GraphQL, Kafka, API Gateway, Cypress, UI testing patterns ### Pact Broker & CI/CD - `references/pact-broker-advanced.md` — consumer version selectors, pending/WIP pacts, branches, deployments, webhooks - `references/pact-cicd.md` — Pact Nirvana CI/CD guide (Bronze → Diamond), can-i-deploy deep-dive ### PactFlow & MCP Tools - `references/workflow.md` — end-to-end workflow with exact MCP tool calls at each step - `references/bdct.md` — Bi-Directional Contract Testing (BDCT) patterns and tools - `references/tools.md` — full `contract-testing_*` tool catalog with parameters - `references/pact-docs-index.md` — complete index of all docs.pact.io documentation with URLs --- ## MCP Setup If the `contract-testing_*` tools are not available, the user needs to install and configure the SmartBear MCP server first. Read `references/mcp-setup.md` for full instructions covering: - Authentication (PactFlow API token or Pact Broker username/password) - Configuration for Claude Code, Claude Desktop, VS Code, and Cursor - Environment variables: `PACT_BROKER_BASE_URL`, `PACT_BROKER_TOKEN` - Verifying the connection works --- ## Core Concepts **Pacticipant** — any application (consumer or provider) registered in the workspace. Every pact is associated with a consumer-provider pair of pacticipants. **Pact** — a contract file generated by the consumer's test suite. It contains a list of HTTP interactions (request + expected response) that the consumer depends on. **Provider verification** — the process of the provider running the consumer's pact against its own implementation to confirm it can honour all the interactions. **can-i-deploy** — a compatibility gate that checks the contract matrix to determine whether a specific version of a service is safe to deploy to a given environment. Run it in CI _before_ deploying. **Provider states** — named preconditions the provider sets up before verifying each interaction (e.g. "a user with id 123 exists"). Use [REDACTED_SECRET] to discover existing states before writing new consumer tests — this avoids duplication and promotes collaboration. **Environments** — named deployment targets (e.g. `staging`, `production`) with UUIDs. Record deployments against them so can-i-deploy reflects real state. **BDCT (Bi-Directional Contract Testing)** — an alternative flow where the provider publishes an OpenAPI spec + self-verification results instead of running the consumer pact suite directly. PactFlow performs cross-contract verification automatically. See `references/bdct.md`. --- ## Standard Pact Workflow The typical flow for a consumer-driven contract testing setup: ``` Consumer tests run ↓ Pact file generated ↓ contract-testing_publish_consumer_contracts ← upload pact + branch/version ↓ Provider CI picks up pact via: contract-testing_get_pacts_for_verification ← fetch pacts to verify ↓ Provider verifies pact against implementation ↓ contract-testing_can_i_deploy ← gate before deploy ↓ Deploy ↓ contract-testing_record_deployment ← update workspace state ``` Read `references/workflow.md` for exact parameters at each step. --- ## PactFlow AI Features (Cloud only) Use these when the user wants to **create or improve Pact tests with AI**: **Generate tests** — [REDACTED_SECRET] - Accepts: request/response pairs, code files, or an OpenAPI document + matcher - Returns: complete Pact test in the target language - Always fetch provider states first ([REDACTED_SECRET]) so the generated tests reuse existing state names **Review tests** — [REDACTED_SECRET] - Accepts: existing pact test files, optional code context, optional error output from failed runs - Returns: ranked list of recommendations aligned with best practices - Useful for auditing older tests or fixing a broken verification If either AI tool returns a 401 error, call [REDACTED_SECRET] to diagnose credit or permission issues. --- ## Diagnosing Can-i-deploy Failures When `can-i-deploy` fails with "no verified pact between X and the version of Y deployed to production", work through this in order: **1. Identify what is actually deployed in production** ``` contract-testing_get_currently_deployed_versions → environmentId: <prod-uuid> ``` **2. Inspect the Pact Matrix** — this is the source of truth for what has/hasn't been verified ``` contract-testing_matrix q: [{pacticipant: "ConsumerName", version: "abc123"}, {pacticipant: "ProviderName", deployed: true, environment: "production"}] ``` - No row → provider never verified this consumer version - Row with `false` → verification ran and failed (genuine contract break) - Row with `true` → check other integrated services **3. Fix based on root cause** | Root cause | Fix | |---|---| | Provider not verifying this consumer version | Add `{ deployedOrReleased: true }` to provider's `consumerVersionSelectors` | | Verification results not published | Ensure `publishVerificationResults: true`, `providerVersion: $GIT_COMMIT`, `providerVersionBranch: $GIT_BRANCH` in provider CI | | No webhook firing on pact changes | Create webhook with event [REDACTED_SECRET] | | PaymentService not recorded as deployed | Call [REDACTED_SECRET] for the version currently in production | For deeper investigation, read `references/pact-broker-advanced.md` and `references/pact-cicd.md`. --- ## Writing Consumer Tests and Provider Verification When generating or reviewing Pact tests: **Before writing tests** — check existing provider states and pacticipant names to avoid duplication: ``` contract-testing_get_provider_states → pacticipant: "ProviderName" contract-testing_list_pacticipants → confirm exact names as registered in the broker ``` **Provider verification must-haves** — every provider verification config needs all three selectors and both flags: ```javascript consumerVersionSelectors: [ { mainBranch: true }, // latest from consumer's main branch { matchingBranch: true }, // feature branch pair-testing { deployedOrReleased: true }, // ALL versions currently deployed/released anywhere ], enablePending: true, // new consumer interactions won't break provider CI publishVerificationResults: process.env.CI === "true", // only publish in CI providerVersion: process.env.GIT_COMMIT, providerVersionBranch: process.env.GIT_BRANCH, ``` Missing `deployedOrReleased: true` is the single most common reason `can-i-deploy` fails — the provider never verifies the version that's actually deployed in production. **Optional fields** — Pact has no `optional()` or `nullable()` matcher. Use **two separate interactions** differentiated by provider states: one where the field is present, one where it's absent/null. **Message pacts (Kafka, SQS, SNS) — hexagonal architecture** Split the code into two layers before writing tests: - **Adapter** — the Kafka/SQS listener, `@KafkaListener`, `ConsumerRecord` wiring. Pact does NOT test this. - **Port** — the domain function that receives the deserialized payload. Pact tests THIS directly. The consumer test calls the Port directly with a Pact-generated message. No Kafka broker needed. See `references/pact-messages.md` for full JS and Java examples. --- ## Other Diagnostic Patterns **Provider verification fails** → use [REDACTED_SECRET] with `error_messages` populated from the failing test output. Use [REDACTED_SECRET] to check whether the required state exists on the provider. **"Which version is deployed in staging?"** → [REDACTED_SECRET] with the staging environment UUID. **"What services depend on this provider?"** → [REDACTED_SECRET] to visualise the blast radius. **Investigating a specific consumer-provider failure in BDCT** → start with [REDACTED_SECRET], then drill into [REDACTED_SECRET] for the failing pair. See `references/bdct.md`. --- ## Workspace Management When setting up a new service or onboarding a team: 1. [REDACTED_SECRET] — register the service 2. [REDACTED_SECRET] — set `mainBranch` (e.g. `main`) so branch-based can-i-deploy works correctly 3. [REDACTED_SECRET] — discover existing environment UUIDs 4. `contract-testing_create_environment` — add new environments if needed For auditing and observability: - `contract-testing_get_metrics` — workspace-wide usage statistics - `contract-testing_get_team_metrics` — per-team breakdown (PactFlow Cloud only) - `contract-testing_list_integrations` — all consumer-provider pairings --- ## Deployment vs Release | Scenario | Tool | | ---------------------------------------------------- | --------------------------------------------------- | | Traditional service deployed to an environment | `contract-testing_record_deployment` | | Mobile app / library where multiple versions coexist | `contract-testing_record_release` | | Check what's live in an environment | `contract-testing_get_currently_deployed_versions` | | Check what's supported (mobile/library) | `contract-testing_get_currently_supported_versions` | `record-deployment` replaces the previous deployed version in that environment. `record-release` does not — both versions remain "supported" simultaneously. --- ## Tool Naming All tools in this skill use the `contract-testing_` prefix. When the user is connected to **PactFlow Cloud**, all tools are available. When connected to an **open-source Pact Broker**, only non-Cloud tools are available (AI generation, BDCT, and team metrics require Cloud).

path: evals/evals.json
content: {
  "skill_name": "pactflow",
  "evals": [
    {
      "id": 1,
      "prompt": "I just got access to a PactFlow account at myorg.pactflow.io. How do I get the contract-testing tools working in Claude Code? Walk me through the full setup.",
      "expected_output": "Step-by-step setup guide covering: getting a PactFlow API token, installing/configuring the SmartBear MCP server for Claude Code, setting the required environment variables (PACT_BROKER_BASE_URL, PACT_BROKER_TOKEN), and verifying the connection works.",
      "files": [],
      "assertions": [
        {
          "name": "mentions_smartbear_mcp_package",
          "description": "Response mentions the @smartbear/mcp npm package specifically (not a generic custom MCP server the user must build)",
          "type": "contains",
          "value": "@smartbear/mcp"
        },
        {
          "name": "covers_pact_broker_base_url",
          "description": "Response covers the PACT_BROKER_BASE_URL environment variable",
          "type": "contains",
          "value": "PACT_BROKER_BASE_URL"
        },
        {
          "name": "covers_pact_broker_token",
          "description": "Response covers the PACT_BROKER_TOKEN environment variable",
          "type": "contains",
          "value": "PACT_BROKER_TOKEN"
        },
        {
          "name": "covers_api_token_step",
          "description": "Response includes step to get PactFlow API token from settings page",
          "type": "llm_judge",
          "value": "The response explains how to get a PactFlow API token from the PactFlow settings page or equivalent"
        },
        {
          "name": "covers_verification_step",
          "description": "Response includes a step to verify the connection actually works",
          "type": "llm_judge",
          "value": "The response includes a concrete step to verify or test that the MCP connection is working (e.g. run a command, ask Claude something, use inspector)"
        },
        {
          "name": "no_custom_server_required",
          "description": "Response does NOT tell user to build a custom MCP server from scratch as the primary path",
          "type": "llm_judge",
          "value": "The response does NOT tell the user they need to build their own custom MCP server from scratch as the primary/recommended approach. It uses an existing package like @smartbear/mcp instead."
        }
      ]
    },
    {
      "id": 2,
      "prompt": "Our can-i-deploy check is failing in CI before we deploy the OrderService to production. The error says 'There is no verified pact between version abc123 of OrderService and the version of PaymentService currently deployed to production.' We're using branches and environments. How do I investigate and fix this?",
      "expected_output": "Explanation of why can-i-deploy fails, how to use contract-testing_matrix to investigate, what consumer version selectors to check, and concrete steps to resolve: either deploy the provider with matching verification, or ensure deployedOrReleased selector is set on the provider.",
      "files": [],
      "assertions": [
        {
          "name": "explains_matrix",
          "description": "Response explains the Pact Matrix concept and how it relates to the failure",
          "type": "llm_judge",
          "value": "The response explains the Pact Matrix (the table of consumer-provider version pairs with verification results) and why it's the key thing to check"
        },
        {
          "name": "mentions_deployed_or_released_selector",
          "description": "Response mentions the deployedOrReleased consumer version selector as a potential fix",
          "type": "contains",
          "value": "deployedOrReleased"
        },
        {
          "name": "covers_investigation_steps",
          "description": "Response gives specific steps to investigate (not just the fix)",
          "type": "llm_judge",
          "value": "The response gives concrete investigation steps to diagnose the root cause (e.g. check the matrix, look at verification results, check what version is deployed in production)"
        },
        {
          "name": "covers_fix_steps",
          "description": "Response gives concrete steps to fix the problem",
          "type": "llm_judge",
          "value": "The response gives concrete, actionable steps to fix the failing can-i-deploy check"
        },
        {
          "name": "covers_publish_verification_results",
          "description": "Response mentions publishing verification results from the provider",
          "type": "llm_judge",
          "value": "The response mentions ensuring that provider verification results are published back to the broker (publishVerificationResult, providerVersion, or equivalent)"
        },
        {
          "name": "mentions_webhook_or_trigger",
          "description": "Response mentions webhooks or triggering provider CI to verify the consumer pact",
          "type": "llm_judge",
          "value": "The response mentions using webhooks or triggering the provider's CI pipeline to verify the consumer's pact as a resolution or prevention step"
        }
      ]
    },
    {
      "id": 3,
      "prompt": "I need to write Pact consumer tests in JavaScript for a REST API client that calls GET /orders/:id and POST /orders. The provider is called OrderAPI, my consumer is CheckoutApp. The GET response has an optional discount field that's sometimes null. Show me the full test file and the provider state handlers the backend team needs.",
      "expected_output": "Complete JavaScript consumer test file using @pact-foundation/pact with proper matchers (like, eachLike, term), two interactions (GET and POST), two separate interactions for the optional discount field (present vs absent via different provider states), and matching provider state handler code.",
      "files": [],
      "assertions": [
        {
          "name": "complete_consumer_test_file",
          "description": "Response includes a complete, runnable consumer test file (not just snippets)",
          "type": "llm_judge",
          "value": "The response includes a complete consumer test file with imports, provider setup, and multiple interactions — not just code snippets"
        },
        {
          "name": "uses_type_matchers",
          "description": "Response uses like() or eachLike() type matchers rather than exact values for all fields",
          "type": "llm_judge",
          "value": "The consumer test uses type matchers like like() or eachLike() for response fields rather than exact value matching everywhere"
        },
        {
          "name": "two_interactions_for_optional_discount",
          "description": "Response uses two separate interactions (one with discount, one without) rather than an inline nullable matcher",
          "type": "llm_judge",
          "value": "The response uses two separate pact interactions to handle the optional discount field: one interaction where discount is present (non-null) and one where it is null/absent, differentiated by provider states — NOT a single interaction with an inline nullable or optional matcher"
        },
        {
          "name": "includes_provider_state_handlers",
          "description": "Response includes provider state handler code (stateHandlers object) for the backend team",
          "type": "llm_judge",
          "value": "The response includes concrete provider state handler code for the OrderAPI backend team to implement, with stateHandlers matching the state names used in the consumer test"
        },
        {
          "name": "provider_verification_includes_deployed_or_released",
          "description": "The provider verification config includes the deployedOrReleased: true consumer version selector",
          "type": "llm_judge",
          "value": "The provider verification setup includes { deployedOrReleased: true } as one of the consumerVersionSelectors (not just mainBranch or matchingBranch)"
        },
        {
          "name": "provider_verification_enables_pending",
          "description": "The provider verification config includes enablePending: true",
          "type": "llm_judge",
          "value": "The provider verification setup includes enablePending: true so new consumer interactions don't break the provider's CI build"
        }
      ]
    },
    {
      "id": 4,
      "prompt": "We have a Kafka consumer in Java that reads order events from a topic. How do I write a Pact message test for it? I want to make sure the consumer can handle the message structure the producer sends.",
      "expected_output": "Complete Java JUnit5 message consumer test using PactConsumerTestExt with ProviderType.ASYNCH, PactDslJsonBody body definition, and matching provider (producer) verification test using @PactVerifyProvider annotation. Should explain the hexagonal architecture pattern for message pact testing.",
      "files": [],
      "assertions": [
        {
          "name": "uses_pact_consumer_test_ext",
          "description": "Consumer test uses @ExtendWith(PactConsumerTestExt.class) or equivalent JUnit5 extension",
          "type": "llm_judge",
          "value": "The consumer test uses PactConsumerTestExt (the JUnit5 extension for Pact consumer tests) via @ExtendWith annotation"
        },
        {
          "name": "uses_asynch_provider_type",
          "description": "Test specifies ProviderType.ASYNCH (not default HTTP pact type)",
          "type": "contains",
          "value": "ASYNCH"
        },
        {
          "name": "includes_consumer_test",
          "description": "Response includes a complete consumer-side message test",
          "type": "llm_judge",
          "value": "The response includes a complete Java consumer test that defines the expected message structure and verifies the consumer handler can process it"
        },
        {
          "name": "includes_provider_verification",
          "description": "Response includes provider-side verification using @PactVerifyProvider",
          "type": "llm_judge",
          "value": "The response includes provider-side verification code with @PactVerifyProvider annotation (or equivalent) to prove the producer can generate the expected message"
        },
        {
          "name": "explains_hexagonal_architecture",
          "description": "Response explains the hexagonal architecture / adapter-port pattern for message pact testing",
          "type": "llm_judge",
          "value": "The response explains or references the hexagonal architecture pattern: that the Pact test exercises the message handler (the Port) directly, bypassing the Kafka transport layer (the Adapter)"
        },
        {
          "name": "uses_message_pact_builder_or_dsl",
          "description": "Response uses MessagePactBuilder, PactDslJsonBody, or equivalent DSL for defining the message body",
          "type": "llm_judge",
          "value": "The response uses MessagePactBuilder, PactDslJsonBody, or an equivalent DSL to define the expected message body with type matchers"
        }
      ]
    }
  ]
}


path: references/dsl.typescript.md
content: While you already know this, here is a reminder of the Pact-JS classes and methods you will need to use to create a Pact test in TypeScript (having omitted deprecated and unadvised methods):

File: /v3/pact.ts
```
// import { PactV3 } from "@pact-foundation/pact";
class PactV3 {
    constructor(opts: PactV3Options);
    addInteraction(interaction: V3Interaction): PactV3;
    given(providerState: string, parameters?: JsonMap): PactV3;
    uponReceiving(description: string): PactV3;
    withRequest(req: V3Request): PactV3;
    withRequestBinaryFile(req: V3Request, contentType: string, file: string): PactV3;
    withRequestMultipartFileUpload(req: V3Request, contentType: string, file: string, mimePartName: string): PactV3;
    willRespondWith(res: V3Response): PactV3;
    withResponseBinaryFile(res: V3Response, contentType: string, file: string): PactV3;
    withResponseMultipartFileUpload(res: V3Response, contentType: string, file: string, mimePartName: string): PactV3;
    executeTest<T>(testFn: (mockServer: V3MockServer) => Promise<T>): Promise<T | undefined>;
}
```

File: /v3/matchers.ts
```
// import { MatchersV3 } from "@pact-foundation/pact";
module MatchersV3 {
    function isMatcher(x: unknown): x is Matcher<unknown>;
    const like: <T>(template: T) => Matcher<T>;
    const eachKeyLike: <T>(keyTemplate: string, template: T) => Matcher<T>;
    const eachKeyMatches: (example: Record<string, unknown>, matchers?: Matcher<string> | Matcher<string>[]) => RulesMatcher<unknown>;
    const eachValueMatches: <T>(example: Record<string, T>, matchers: Matcher<T> | Matcher<T>[]) => RulesMatcher<T>;
    const eachLike: <T>(template: T, min?: number) => MinLikeMatcher<T[]>;
    const atLeastOneLike: <T>(template: T, count?: number) => MinLikeMatcher<T[]>;
    const atLeastLike: <T>(template: T, min: number, count?: number) => MinLikeMatcher<T[]>;
    const atMostLike: <T>(template: T, max: number, count?: number) => MaxLikeMatcher<T[]>;
    const constrainedArrayLike: <T>(template: T, min: number, max: number, count?: number) => MinLikeMatcher<T[]> & MaxLikeMatcher<T[]>;
    const boolean: (b?: boolean) => Matcher<boolean>;
    const integer: (int?: number) => Matcher<number>;
    function number(num?: number): Matcher<number>;
    function string(str?: string): Matcher<string>;
    function regex(pattern: RegExp | string, str: string): V3RegexMatcher;
    const equal: <T>(value: T) => Matcher<T>;
    function datetime(format: string, example: string): DateTimeMatcher;
    function timestamp(format: string, example: string): DateTimeMatcher;
    function time(format: string, example: string): DateTimeMatcher;
    function date(format: string, example: string): DateTimeMatcher;
    function includes(value: string): Matcher<string>;
    function nullValue(): Matcher<null>;
    function url2(basePath: string | null, pathFragments: Array<string | V3RegexMatcher | RegExp>): V3RegexMatcher;
    function url(pathFragments: Array<string | V3RegexMatcher | RegExp>): V3RegexMatcher;
    function arrayContaining(...variants: unknown[]): ArrayContainsMatcher;
    function fromProviderState<V>(expression: string, exampleValue: V): ProviderStateInjectedValue<V>;
    function uuid(example?: string): V3RegexMatcher;
    const matcherValueOrString: (obj: unknown) => string;
    function reify(input: unknown): AnyJson;
    const extractPayload: typeof reify;
}
```

File: /v3/xml/xmlBuilder.ts
```
class XmlBuilder {
    build(callback: (doc: XmlElement) => void): string;
}
```

File: /v3/xml/xmlElement.ts
```
type XmlAttributes = Map<string, string>;
type XmlCallback = (n: XmlElement) => void;

class XmlElement {
    setName(name: string): XmlElement;
    setAttributes(attributes: XmlAttributes): XmlElement;
    appendElement(name: string, attributes: XmlAttributes, arg?: string | XmlCallback | Matcher<string>): XmlElement;
    appendText(content: string | Matcher<string>): XmlElement;
    eachLike(name: string, attributes: XmlAttributes, cb?: XmlCallback, options: EachLikeOptions = { examples: 1 }): XmlElement;
}

interface EachLikeOptions {
    min?: number;
    max?: number;
    examples?: number;
}
```

File: /v3/xml/xmlNode.ts
```
class XmlNode {}
```

File: /v3/xml/xmlText.ts
```
class XmlText {
    constructor(content: string, matcher?: Matcher<string>);
}
```

File: /dsl/interaction.ts
```
interface QueryObject {
  [name: string]: string | Matcher<string> | string[];
}
type Query = string | QueryObject;
type Headers = {
  [header: string]: string | Matcher<string> | (Matcher<string> | string)[];
};
interface RequestOptions {
  method: HTTPMethods | HTTPMethod;
  path: string | Matcher<string>;
  query?: Query;
  headers?: Headers;
  body?: AnyTemplate;
}
interface ResponseOptions {
  status: number;
  headers?: Headers;
  body?: AnyTemplate;
}
interface InteractionObject {
  state: string | undefined;
  uponReceiving: string;
  withRequest: RequestOptions;
  willRespondWith: ResponseOptions;
}
interface InteractionState {
  providerState?: string;
  description?: string;
  request?: RequestOptions;
  response?: ResponseOptions;
}
interface InteractionStateComplete {
  providerState?: string;
  description: string;
  request: RequestOptions;
  response: ResponseOptions;
}
class Interaction {
  given(providerState: string): this;
  uponReceiving(description: string): this;
  withRequest(requestOpts: RequestOptions): this;
  willRespondWith(responseOpts: ResponseOptions): this;
  json(): InteractionStateComplete;
}
const interactionToInteractionObject(interaction: InteractionStateComplete): InteractionObject;
```

File: /dsl/options.ts
```
type LogLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error';
interface PactOptions {
    consumer: string;
    provider: string;
    port?: number;
    host?: string;
    ssl?: boolean;
    sslcert?: string;
    sslkey?: string;
    dir?: string;
    log?: string;
    logLevel?: LogLevel;
    spec?: number;
    cors?: boolean;
    timeout?: number;
    pactfileWriteMode?: PactfileWriteMode;
}
interface MandatoryPactOptions {
    port: number;
    host: string;
    ssl: boolean;
}
type PactOptionsComplete = PactOptions & MandatoryPactOptions;
interface MessageProviderOptions {
    logLevel?: LogLevel;
    messageProviders: MessageProviders;
    stateHandlers?: MessageStateHandlers;
}
type ExcludedPactNodeVerifierKeys = Exclude<keyof PactCoreVerifierOptions, 'providerBaseUrl'>;
type PactNodeVerificationExcludedOptions = Pick<PactCoreVerifierOptions, ExcludedPactNodeVerifierKeys>;
type PactMessageProviderOptions = PactNodeVerificationExcludedOptions & MessageProviderOptions;
interface MessageConsumerOptions {
    consumer: string;
    dir?: string;
    provider: string;
    log?: string;
    logLevel?: LogLevel;
    spec?: number;
    pactfileWriteMode?: PactfileWriteMode;
}
```

File: /dsl/verifier/verifier.ts
```
class Verifier {
    constructor(config: VerifierOptions);
    verifyProvider(): Promise<string>;
}
```

File: /dsl/verifier/proxy/messages.ts
```
export const providerWithMetadata(provider: MessageProvider, metadata: Record<string, string>): MessageProvider;
```

File: /dsl/verifier/proxy/types.ts
```
type Hook: () => Promise<unknown>;
interface StateHandlers {
  [name: string]: StateHandler;
}
interface ProviderState {
  action: StateAction;
  params: JsonMap;
  state: string;
}
type StateAction: 'setup' | 'teardown';
type StateFunc: (parameters?: AnyJson) => Promise<JsonMap | void>;
type StateFuncWithSetup: {
  setup?: StateFunc;
  teardown?: StateFunc;
};
type StateHandler: StateFuncWithSetup | StateFunc;
```


path: references/mcp-setup.md
content: # SmartBear MCP Server — Installation & Configuration

> Source: https://github.com/SmartBear/smartbear-mcp

The `@smartbear/mcp` package exposes all `contract-testing_*` tools as a Model Context Protocol (MCP) server. You need to install and configure it before any of those tools are available.

---

## Prerequisites

- **Node.js 20+** — required to run the MCP server
- **PactFlow account** (Cloud) or a self-hosted **Pact Broker** instance
- An API token or username/password for your broker

---

## Authentication

### PactFlow Cloud

1. Go to `app.pactflow.io/settings/api-tokens`
2. Create a read/write token
3. Copy the token value — you'll use it as `PACT_BROKER_TOKEN`

### Open-source Pact Broker

Use `PACT_BROKER_USERNAME` and `PACT_BROKER_PASSWORD` instead of a token.

---

## Environment Variables

| Variable               | Required for       | Description                                                 |
| ---------------------- | ------------------ | ----------------------------------------------------------- |
| `PACT_BROKER_BASE_URL` | All                | Full URL to your broker, e.g. `https://yourorg.pactflow.io` |
| `PACT_BROKER_TOKEN`    | PactFlow Cloud     | API token from the settings page                            |
| `PACT_BROKER_USERNAME` | Open-source broker | HTTP Basic Auth username                                    |
| `PACT_BROKER_PASSWORD` | Open-source broker | HTTP Basic Auth password                                    |

Use token **or** username/password — not both.

---

## Configuration by Client

### Claude Code (CLI)

```bash
claude mcp add --transport stdio smartbear node mcp
```

Then set environment variables in your shell profile (`~/.zshrc`, `~/.bashrc`):

**PactFlow Cloud (token):**

```bash
export PACT_BROKER_BASE_URL="https://yourorg.pactflow.io"
export PACT_BROKER_TOKEN="your-api-token"
```

**Open-source Pact Broker (username/password):**

```bash
export PACT_BROKER_BASE_URL="https://your-self-hosted-broker.example.com"
export PACT_BROKER_USERNAME="your-username"
export PACT_BROKER_PASSWORD="your-password"
```

Or pass them inline when starting a session:

```bash
PACT_BROKER_BASE_URL=https://yourorg.pactflow.io \
PACT_BROKER_TOKEN=your-token \
claude
```

### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

**PactFlow Cloud (token):**

```json
{
  "mcpServers": {
    "smartbear": {
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://yourorg.pactflow.io",
        "PACT_BROKER_TOKEN": "your-api-token"
      }
    }
  }
}
```

**Open-source Pact Broker (username/password):**

```json
{
  "mcpServers": {
    "smartbear": {
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://your-self-hosted-broker.example.com",
        "PACT_BROKER_USERNAME": "your-username",
        "PACT_BROKER_PASSWORD": "your-password"
      }
    }
  }
}
```

### VS Code (with Copilot or Claude extension)

Create or edit `.vscode/mcp.json` in your project root:

**PactFlow Cloud (token):**

```json
{
  "servers": {
    "smartbear": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://yourorg.pactflow.io",
        "PACT_BROKER_TOKEN": "your-api-token"
      }
    }
  }
}
```

**Open-source Pact Broker (username/password):**

```json
{
  "servers": {
    "smartbear": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://your-self-hosted-broker.example.com",
        "PACT_BROKER_USERNAME": "your-username",
        "PACT_BROKER_PASSWORD": "your-password"
      }
    }
  }
}
```

### Cursor

Edit `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):

**PactFlow Cloud (token):**

```json
{
  "mcpServers": {
    "smartbear": {
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://yourorg.pactflow.io",
        "PACT_BROKER_TOKEN": "your-api-token"
      }
    }
  }
}
```

**Open-source Pact Broker (username/password):**

```json
{
  "mcpServers": {
    "smartbear": {
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://your-self-hosted-broker.example.com",
        "PACT_BROKER_USERNAME": "your-username",
        "PACT_BROKER_PASSWORD": "your-password"
      }
    }
  }
}
```

---

## Global Install (optional)

If you prefer a global install over `npx`:

```bash
npm install -g @smartbear/mcp
```

Then replace `"command": "npx", "args": ["-y", "@smartbear/mcp@latest"]` with `"command": "node", "args": ["mcp"]` in the configs above, or just use:

```bash
# Claude Code global install variant
claude mcp add --transport stdio smartbear node mcp
```

---

## Verify the Connection

### Using MCP Inspector

```bash
npx @modelcontextprotocol/inspector npx @smartbear/mcp@latest
```

This opens a browser UI where you can call tools directly to confirm authentication is working.

### Quick check via Claude Code

Once configured, ask Claude:

```
List the environments in my PactFlow workspace
```

Claude will call `contract-testing_list_environments` and return the results if the connection is working.

---

## Troubleshooting

| Symptom               | Likely cause                    | Fix                                                                  |
| --------------------- | ------------------------------- | -------------------------------------------------------------------- |
| `401 Unauthorized`    | Wrong or missing token          | Check `PACT_BROKER_TOKEN` is set and matches the API token           |
| `404 Not Found`       | Wrong broker URL                | Verify `PACT_BROKER_BASE_URL` — no trailing slash, correct subdomain |
| Tools not showing     | MCP server not starting         | Run `npx @smartbear/mcp@latest` in a terminal to see startup errors  |
| AI tools return `401` | Missing PactFlow AI entitlement | Call `contract-testing_check_pactflow_ai_entitlements` to diagnose   |

---

## Open-source Pact Broker config example

```json
{
  "mcpServers": {
    "smartbear": {
      "command": "npx",
      "args": ["-y", "@smartbear/mcp@latest"],
      "env": {
        "PACT_BROKER_BASE_URL": "https://your-self-hosted-broker.example.com",
        "PACT_BROKER_USERNAME": "admin",
        "PACT_BROKER_PASSWORD": "your-password"
      }
    }
  }
}
```

Note: AI generation, BDCT, and team metrics tools require PactFlow Cloud and will not work with an open-source Pact Broker.


path: references/pact-plugins.md
content: # Pact Plugin Framework

> Sources: https://docs.pact.io/plugins/quick_start.md, https://docs.pact.io/plugins/directory.md, https://docs.pact.io/implementation_guides/pact_plugins.md, https://docs.pact.io/implementation_guides/javascript/docs/plugins.md

---

## What is the Plugin Framework?

The Pact Plugin Framework extends Pact beyond HTTP and JSON to support custom **transports** and **protocols**. It was created to handle use cases that the core Pact framework couldn't support out of the box.

**Examples of what plugins enable:**

- gRPC (over HTTP/2)
- Protobuf messages
- WebSockets / TCP
- CSV content matching
- Avro messages
- GraphQL custom handling
- Any proprietary protocol

---

## How It Works

Plugins communicate with the Pact core via gRPC. When a test runs:

1. Your test uses `usingPlugin(name, version)` to declare a plugin dependency
2. The **Plugin Driver** (built into the Pact Rust core / JVM driver) discovers and starts the plugin process
3. The plugin starts a gRPC server and registers its capabilities (content matchers, mock servers, verifiers)
4. Pact routes interactions through the plugin for both consumer mock generation and provider verification

**Interaction types** work with plugins:

- `Synchronous/HTTP` — standard HTTP, plugin provides custom content matching or request/response handling
- `Asynchronous/Messages` — one-way events (Kafka, SNS, etc.), plugin handles message format
- `Synchronous/Messages` — request/response over non-HTTP (gRPC, WebSocket, TCP)

---

## Available Plugins

| Plugin                                                                                 | Type            | Language | Support             |
| -------------------------------------------------------------------------------------- | --------------- | -------- | ------------------- |
| [`pact-protobuf-plugin`](https://github.com/pactflow/pact-protobuf-plugin)             | Protobuf + gRPC | Rust     | Official (PactFlow) |
| [`pact-csv-plugin`](https://github.com/pact-foundation/pact-plugins/tree/main/plugins) | CSV content     | Rust     | Official            |
| [`pact-avro-plugin`](https://github.com/austek/pact-avro-plugin)                       | Avro messages   | Scala    | Community           |

---

## Installing Plugins

Plugins are installed into `$HOME/.pact/plugins/<plugin-name>-<version>/`.

### Via the Pact Plugin CLI

```bash
# Install the Pact Plugin CLI
# Download from: https://github.com/pact-foundation/pact-plugins/releases

# Install a plugin
pact-plugin-cli install pact-protobuf-plugin

# List installed plugins
pact-plugin-cli list

# Remove a plugin
pact-plugin-cli remove pact-protobuf-plugin 1.0.0
```

### Manual installation

1. Download the plugin release archive from the plugin's GitHub releases
2. Extract to `$HOME/.pact/plugins/<plugin-name>-<version>/`
3. Ensure the `pact-plugin.json` manifest is present in that directory

---

## Using Plugins in Tests

### JavaScript (Pact JS)

#### HTTP with a custom content type

```typescript
import { Pact } from "@pact-foundation/pact";

const pact = new Pact({ consumer: "MyConsumer", provider: "MyProvider" });

await pact
  .addInteraction()
  .given("gRPC service is available")
  .uponReceiving("an HTTP request with custom content")
  .usingPlugin({
    // Declare the plugin — must come before request/response
    plugin: "matt",
    version: "0.1.1",
  })
  .withRequest("POST", "/api", (builder) => {
    builder.pluginContents(
      "application/matt",
      `{"request": {"body": "hello"}}`,
    );
  })
  .willRespondWith(200, (builder) => {
    builder.pluginContents(
      "application/matt",
      `{"response": {"body": "world"}}`,
    );
  })
  .executeTest(async (mockserver) => {
    const response = await callApi(mockserver.url, "hello");
    expect(response).to.eq("world");
  });
```

#### Asynchronous message with plugin

```typescript
await pact
  .addAsynchronousInteraction()
  .given("the service is up")
  .usingPlugin({ plugin: "matt", version: "0.1.1" })
  .expectsToReceive("an async MATT message")
  .withPluginContents(`{"response": {"body": "tcpworld"}}`, "application/matt")
  .executeTest(async (message) => {
    const content = Buffer.from(
      String(message?.contents?.content || ""),
      "base64",
    ).toString();
    expect(parseMattMessage(content)).to.eq("tcpworld");
  });
```

#### Synchronous message with custom transport (TCP, gRPC)

```typescript
await pact
  .addSynchronousInteraction("a MATT sync message")
  .usingPlugin({ plugin: "matt", version: "0.1.1" })
  .withPluginContents(
    `{"request": {"body": "hello"}, "response": {"body": "world"}}`,
    "application/matt",
  )
  .startTransport("matt", HOST) // Start the transport (TCP/gRPC) on the given host
  .executeTest(async (tc) => {
    const message = await sendMessageTCP("hello", HOST, tc.port);
    expect(message).to.eq("world");
  });
```

#### Provider verification with plugins (JS)

```javascript
const { Verifier } = require("@pact-foundation/pact");

new Verifier({
  providerBaseUrl: `http://${HOST}:${httpPort}`,
  transports: [
    {
      port: tcpPort,
      protocol: "matt",
      scheme: "tcp",
    },
  ],
  pactUrls: ["./pacts/myconsumer-myprovider.json"],
  messageProviders: {
    "an async MATT message": providerWithMetadata(() => Buffer.from("world"), {
      contentType: "application/matt",
    }),
  },
}).verifyProvider();
```

For HTTP-only plugin interactions, provider verification is **identical** to standard HTTP verification — plugins are loaded automatically.

---

### Java / JVM — Protobuf Example

```java
// Consumer test for a Protobuf message
builder
  .usingPlugin("protobuf")                                           // load the protobuf plugin
  .expectsToReceive("init plugin message", "core/interaction/message")
  .with(Map.of(
    "message.contents", Map.of(
      "pact:proto", filePath("../proto/plugin.proto"),              // path to .proto file
      "pact:message-type", "InitPluginRequest",                     // the message type to test
      "pact:content-type", "application/protobuf",
      "implementation", "notEmpty('pact-jvm-driver')",              // matching expression
      "version", "matching(semver, '0.0.0')"                        // semantic version matcher
    )
  ))
  .toPact();
```

**Key fields for Protobuf plugin**:

- `pact:proto` — path to the `.proto` file (relative to test file)
- `pact:message-type` — the Protobuf message type to test
- `pact:content-type` — must be `application/protobuf`
- Field values use **matching rule expressions** (not standard Pact DSL matchers)

**Matching rule expression syntax** (used in Protobuf/plugin contexts):

- `notEmpty('value')` — field must not be empty, use `'value'` as example
- `matching(semver, '1.0.0')` — must match semver format
- `matching(regex, 'pattern', 'example')` — regex match
- `matching(type, null)` — type match
- `matching(equalTo, 'VALUE')` — exact match
- `eachValue(matching($'items'))` — each value in a repeated field matches template

**Nested messages**: use a `Map` for nested message fields. **Repeated/map fields**: include a `pact:match` entry with an expression like `eachValue(matching(type, null))`.

#### Provider verification for Protobuf

Use the `pact_verifier_cli` (must be v0.9.0+) pointing at the running provider:

```bash
pact_verifier_cli \
  -f ./pacts/consumer-provider.json \
  -p 8111
```

Or in JVM, the standard `@TestTemplate` + `PactVerificationInvocationContextProvider` setup works — plugins are auto-loaded.

---

## Plugin Matching Expressions

Plugins typically use the [Matching Rule Definition Expression](https://github.com/pact-foundation/pact-plugins/blob/main/docs/matching-rule-definition-expressions.md) syntax rather than the language-native DSL matchers:

| Expression                                       | Meaning                                                       |
| ------------------------------------------------ | ------------------------------------------------------------- |
| `notEmpty('example')`                            | Field must not be empty; use `example` as the generated value |
| `matching(type, 'example')`                      | Match by type only (not value)                                |
| `matching(equalTo, 'exact')`                     | Exact value match                                             |
| `matching(regex, 'pattern', 'example')`          | Regex pattern match                                           |
| `matching(semver, '1.0.0')`                      | Semantic version format                                       |
| `matching(contentType, 'mime', 'example')`       | Content-type-aware matching                                   |
| `eachKey(matching(regex, 'pattern', 'example'))` | Apply matcher to each key in a map                            |
| `eachValue(matching(type, null))`                | Apply matcher to each value in a repeated/map field           |
| `atLeast(N)` / `atMost(N)`                       | Array length constraints                                      |

---

## Example Demos

| Type     | Scenario         | Languages                              |
| -------- | ---------------- | -------------------------------------- |
| gRPC     | Area Calculator  | Java (Gradle/Maven), Rust, Go          |
| Protobuf | Plugin interface | Java, Rust (consumer); Go (provider)   |
| CSV      | Content type     | Java, Rust (consumer); Rust (provider) |

Interactive tutorial (gRPC, Protobuf, CSV in Go/Rust/Java):
https://killercoda.com/safdotdev/course/safacoda/grpc_quick_start

---

## Building Your Own Plugin

If no existing plugin covers your use case, you can build one:

1. **Template**: Start with the [Go plugin template](https://github.com/pact-foundation/pact-plugin-template-golang) — includes all boilerplate
2. **Workshop**: Step-by-step tutorial at https://killercoda.com/pactflow/scenario/create-a-plugin (runs in browser)
3. **Docs**:
   - [Writing a plugin guide](https://docs.pact.io/implementation_guides/pact_plugins/docs/writing-plugin-guide)
   - [Protocol design](https://docs.pact.io/implementation_guides/pact_plugins/docs/protocol-plugin-design) — for new transports
   - [Content matcher design](https://docs.pact.io/implementation_guides/pact_plugins/docs/content-matcher-design) — for new content types

Plugins communicate via gRPC. They must:

- Provide a `pact-plugin.json` manifest describing the plugin
- Start a gRPC server on load and return the port to the driver
- Register capabilities in a catalogue (matchers, mock servers, verifiers)
- Respond to driver messages for mock generation and verification


path: references/pact-cicd.md
content: # Pact CI/CD Setup Guide — Pact Nirvana

> Sources: https://docs.pact.io/pact_nirvana.md, https://docs.pact.io/pact_broker/can_i_deploy.md, steps 1–7

This guide covers the progressive steps to a full contract testing CI/CD pipeline that enables **independent deployments** with confidence.

---

## The Goal

By the end of this setup, you will have a release pipeline that allows you to independently deploy any application with the confidence that it will work correctly with the other applications in its environment — without running a suite of end-to-end tests.

---

## Step 1: Learn About Pact (Bronze Prep)

Before starting, every team member involved should understand:

- [How Pact works](https://docs.pact.io/getting_started/how_pact_works)
- [Consumer best practices](https://docs.pact.io/consumer)
- [Provider best practices](https://docs.pact.io/provider)
- [Pact Nirvana progression](https://docs.pact.io/pact_nirvana)

---

## Step 2: Get Team Alignment

Contract tests are only valuable if the provider verifies them. Before writing tests:

- **Consumer team**: Explain what you want to test and why. Share the pact with the provider team before they implement it.
- **Provider team**: Commit to running pact verification in CI. Agree on provider state naming conventions.
- **Both teams**: Agree on a shared Pact Broker/PactFlow instance. Discuss the deployment workflow — branches, environments, webhooks.

Contract tests require **coordination** — a consumer test with no provider verification is useless.

---

## Step 3: Bronze — Single Test Working Manually

Goal: Get the consumer-provider contract testing working locally.

**Consumer side**:

1. Write a Pact test for the first interaction
2. Run the test → generates a `pact.json` file
3. Share the pact file with the provider (copy to provider's pacts directory)

**Provider side**:

1. Write a provider state handler for each state referenced in the pact
2. Run pact verification pointing at the local pact file
3. Fix any failures

**Do not** try to automate or integrate with CI yet. Just make it work manually first.

---

## Step 4: Silver — Integrate with Pact Broker

Goal: Automate pact sharing via the Pact Broker.

**Consumer CI**:

1. Run consumer tests → generate pact file
2. Publish pact to broker with version and branch:
   ```
   contract-testing_publish_consumer_contracts
     pacticipantName: "FrontendApp"
     pacticipantVersionNumber: "$GIT_SHA"
     branch: "$GIT_BRANCH"
     contracts: [...]
   ```

**Provider CI**:

1. Fetch pacts from broker using consumer version selectors:
   ```
   contract-testing_get_pacts_for_verification
     providerName: "UserService"
     consumerVersionSelectors: [{ mainBranch: true }]
   ```
2. Run verification against the fetched pacts
3. Publish verification results back to broker (done automatically by the Pact library)

At this stage: no can-i-deploy yet, no deployment recording. Just exchange pacts and verification results.

---

## Step 5: Gold — Integrate with PR Pipelines

Goal: Pact tests run automatically on every PR/commit.

**Consumer PR pipeline**:

1. Run consumer tests
2. Publish pact to broker (with current branch)

**Provider PR pipeline**:

1. Fetch pacts for verification (with `matchingBranch: true` in selectors to pick up feature branches)
2. Run verification
3. Publish verification results

```javascript
consumerVersionSelectors: [
  { mainBranch: true },
  { matchingBranch: true }, // picks up consumer feature branches
];
```

Enable `enablePending: true` to prevent consumer changes from breaking the provider build unexpectedly.

---

## Step 6: Platinum — can-i-deploy with Branch Pipelines

Goal: Use can-i-deploy as a deployment gate. Trigger provider builds when pacts change.

### Set up environments

```
contract-testing_list_environments  # check existing environments
contract-testing_create_environment
  name: "production"
  displayName: "Production"
  production: true
```

### Add can-i-deploy before deploying

In each service's deploy script:

```
contract-testing_can_i_deploy
  pacticipant: "FrontendApp"
  version: "$GIT_SHA"
  environment: "production"
```

If it returns success (exit 0), proceed to deploy. If failure, stop and investigate.

### Record deployment after deploying

```
contract-testing_record_deployment
  pacticipantName: "FrontendApp"
  versionNumber: "$GIT_SHA"
  environmentId: "<uuid-of-production>"
```

### Add a webhook to trigger provider builds

Set up a `contract_requiring_verification_published` webhook:

```
contract-testing_create_webhook
  description: "Trigger UserService build when FrontendApp pact changes"
  events: ["contract_requiring_verification_published"]
  consumer: "FrontendApp"
  provider: "UserService"
  request:
    method: "POST"
    url: "https://ci.example.com/api/builds"
    body:
      pact_url: "${pactbroker.pactUrl}"
      branch: "${pactbroker.consumerVersionBranch}"
```

### Add a new provider verification job (webhook-driven)

Add a separate provider CI job that:

1. Receives the pact URL from the webhook
2. Verifies just that pact URL (no selectors needed):
   ```javascript
   pactUrls: [process.env.PACT_URL];
   ```
3. Publishes verification results

This gives fast feedback to the consumer team without waiting for the full provider regression suite.

### Update consumer version selectors to include deployed versions

```javascript
consumerVersionSelectors: [
  { mainBranch: true },
  { matchingBranch: true },
  { deployedOrReleased: true }, // add this once you're recording deployments
];
```

---

## Step 7: Diamond — Independent Deployments

Goal: Full independent deployments. Consumer and provider deploy at their own pace with confidence.

At this stage, your pipeline automatically:

1. Runs consumer tests on PR → publishes pact
2. Triggers provider build via webhook → verifies pact → publishes results
3. Consumer sees verification results in the Pact Broker before merging
4. Before any deploy: `can-i-deploy` checks compatibility with all deployed versions
5. After deploy: `record-deployment` updates the broker

**The Diamond level advantage**: You can now verify that your new consumer version is compatible with the **production version** of the provider (via `{ deployedOrReleased: true }` selector), not just the head version. This is what enables true independent deployments.

---

## Can I Deploy — Deep Dive

> Source: https://docs.pact.io/pact_broker/can_i_deploy.md

### The Pact Matrix

The Matrix is a table of all consumer and provider versions that have been tested against each other, with verification results:

| Foo version (consumer) | Bar version (provider) | Success? |
| ---------------------- | ---------------------- | -------- |
| 22                     | 56 (production)        | true     |
| 23                     | 56 (production)        | true     |
| 24                     | 58                     | true     |
| 25                     | 58                     | false    |

can-i-deploy queries this matrix. If Bar v56 is in production and we want to deploy Foo v23 → check matrix → v23 and v56 were verified successfully → safe to deploy.

### CLI usage

```bash
# Check (before deploying)
pact-broker can-i-deploy \
  --pacticipant Foo \
  --version 23 \
  --to-environment production \
  --broker-base-url $PACT_BROKER_BASE_URL

# Record (after deploying)
pact-broker record-deployment \
  --pacticipant Foo \
  --version 23 \
  --environment production \
  --broker-base-url $PACT_BROKER_BASE_URL
```

Exit code 0 = safe to deploy. Exit code 1 = NOT safe.

### MCP tool

```
contract-testing_can_i_deploy
  pacticipant: "Foo"
  version: "23"
  environment: "production"
```

### Advanced can-i-deploy features

- **Polling**: Wait for verification results to be published (useful when waiting for a webhook-triggered provider build)
- **Ignoring integrations**: `--ignore PROVIDER` skips a new provider that hasn't set up verification yet
- **Dry run**: `--dry-run` shows results but always exits 0 — use to validate pipeline setup before enabling the check

### Using can-i-deploy with tags (legacy)

If not yet using environments:

```bash
pact-broker can-i-deploy \
  --pacticipant Foo \
  --version 173153ae0 \
  --to production  # tag-based
```

For mobile providers with multiple versions in production:

```bash
pact-broker can-i-deploy \
  --pacticipant Bar --version b80e7b1b \
  --pacticipant Foo --all prod \
  --to prod
```

---

## Universal CI/CD Pipeline Considerations

> Source: https://docs.pact.io/pact_nirvana/notes_1.md

If your organisation has a standard CI/CD pipeline used by all projects, and only some projects use Pact:

**Problem**: `can-i-deploy` returns an error if you try to deploy an application version it doesn't know about.

**Solution**: Wrap `can-i-deploy` to succeed when the application is not registered in the Pact Broker:

```bash
pact-broker can-i-deploy --pacticipant $APP --version $VERSION --to-environment production \
  || [[ $? == 1 && "$(pact-broker can-i-deploy ... 2>&1)" == *"does not exist"* ]] && echo "Not in Pact Broker — skipping"
```

Or use `--dry-run` initially to validate the pipeline without blocking deployments.


path: references/pact-faq.md
content: # Pact FAQ & When to Use Pact

> Sources: https://docs.pact.io/faq.md, https://docs.pact.io/faq/convinceme.md, https://docs.pact.io/getting_started/what_is_pact_good_for.md, https://docs.pact.io/getting_started/comparisons.md, https://docs.pact.io/getting_started/testing-scope.md, https://docs.pact.io/consumer/contract_tests_not_functional_tests.md

---

## What is Pact good for?

Pact is most valuable for designing and testing integrations where:

- You (or your team/organisation/partner) control the development of **both** the consumer and the provider
- The requirements of the consumer are going to drive the features of the provider
- It is fantastic for developing and testing intra-organisation microservices

### What is Pact NOT good for?

- **Too many consumers** — APIs where the consumer count is so large that direct relationships can't be maintained between consumer and provider teams (e.g. public APIs)
- **Performance and load testing** — Pact is not a load-testing tool
- **Functional testing of the provider** — that is what the provider's own unit/integration tests should do. Pact is about checking the contents and format of requests and responses
- **When you can't load test data into the provider** without using the API being tested (e.g. public APIs with no test environment)
- **"Pass-through" APIs** — providers that merely proxy requests to downstream services without validating them

### Who implements Pact?

Pact is generally implemented by **developers**, during development. The code-first, white-box nature means whoever writes the tests needs:

- Strong understanding of the code under test
- Ability to write code, use existing test frameworks (Jest, JUnit, etc.)
- Understanding of how to create and inject stubs
- Familiarity with the Pact library itself

---

## FAQ

### Why is there no support for specifying optional attributes?

Pact is based on consumer-driven contracts. The contract represents the _minimum_ that the consumer needs. If you have an optional field, you simply don't include it in the pact — the provider is free to return it or not, and the consumer must handle both cases.

For testing optional fields, use **Provider States**: write one interaction with the field present and one without, using different provider states to drive the data.

See `references/pact-recipes.md` for the "Optional Fields" pattern with Array Contains matchers.

### Can I use Pact to test an API I don't control (e.g. a third-party API)?

Technically yes — but the value is limited. Provider verification requires the provider to run the pact against their implementation. Third-party providers won't do this, so you can't get the full contract testing benefit.

For third-party APIs, Pact tests can still serve as documentation of what you expect from the API, or you can use it one-sidedly with a stub server.

### Can I test multi-part interactions (workflows)?

Pact tests are designed to be **independent interactions**. Each test covers a single request/response pair. You can use provider states to set up the data needed for each interaction, but Pact does not support sequential/stateful workflows within a single test.

### How do I deal with dates and timestamps?

Use regex matchers (e.g. `Pact.term(generate: "2020-01-01", matcher: /\d{4}-\d{2}-\d{2}/)`) or type matchers. Do NOT use dynamic/random dates — this creates a new pact content row every run.

### How do I handle authentication?

See `references/pact-provider.md` — "Handling Authentication" section. Short answer: stub auth services with hardcoded credentials, or use request filters to inject a real token at verification time.

### What's the difference between `can-i-deploy` and just running tests?

`can-i-deploy` checks the **contract matrix** — the full picture of which consumer and provider versions have been verified against each other, and which versions are currently deployed to each environment. It prevents you from deploying a consumer version that is not compatible with the _currently deployed_ provider version in the target environment.

Just running tests locally or in CI tells you if your new version is compatible with the _latest_ provider code. `can-i-deploy` checks compatibility with what's actually running in production.

---

## Contract Tests vs Functional Tests

> Source: https://docs.pact.io/consumer/contract_tests_not_functional_tests.md

This is a critical distinction. Contract tests are **not** functional tests.

### What contract tests check

- The **format** and **structure** of requests and responses
- That the consumer correctly creates requests the provider will understand
- That the consumer correctly handles the response the provider returns

### What contract tests do NOT check

- Business logic ("if premium subscriber, return 20% discount")
- Provider-side validation rules
- Specific data values (unless they genuinely matter to the consumer)
- Side effects on the provider (e.g. that a record was created)
- Full end-to-end workflows

### The golden rule

For every interaction in a contract test, ask:

> "If I removed this test, what bug in the consumer or misunderstanding about the provider might be missed?"

If the answer is "none", remove the test.

### Example: wrong vs right

```
# WRONG: testing provider business logic
"when a user has a premium subscription, the discount should be 20%"

# RIGHT: testing the contract
"given user 123 exists, GET /users/123 returns a user object with id (Integer), name (String), and discountRate (Number)"
```

The consumer doesn't need to assert that the discount is 20%. It needs to assert that there IS a discount field, and that it's a Number it can apply.

---

## Testing Scope

> Source: https://docs.pact.io/getting_started/testing-scope.md

Contract tests sit between unit tests and end-to-end tests in the testing pyramid:

```
        /\
       /  \
      / E2E \      ← Full stack, expensive, brittle
     /--------\
    / Contract \   ← Integration point, cheap, reliable
   /------------\
  /  Unit Tests  \ ← Isolated component logic
 /----------------\
```

**Contract tests** cover the HTTP boundary — they verify that the consumer creates correct requests, and that the provider returns responses the consumer can handle. They do this cheaply and without needing both services running simultaneously.

### What goes where

| Concern                                             | Test type                             |
| --------------------------------------------------- | ------------------------------------- |
| Consumer's HTTP client creates the right request    | Consumer contract test                |
| Provider's handler returns the right response shape | Provider contract test (verification) |
| Business logic in the provider                      | Provider unit tests                   |
| Business logic in the consumer                      | Consumer unit tests                   |
| Services work together end-to-end                   | E2E tests (minimal, targeted)         |

### Testing scope on the consumer side

Only test the API client layer — the code responsible for making HTTP calls and handling responses. Do NOT write Pact tests that also exercise the business logic that uses the response data; that's what unit tests are for.

### Testing scope on the provider side

Only test that the provider can serve the responses the consumer expects. Do NOT duplicate business logic assertions in the contract — the provider has its own test suite for that.

---

## Comparisons

> Source: https://docs.pact.io/getting_started/comparisons.md

### Pact vs End-to-End Tests

|              | E2E Tests                          | Pact                                            |
| ------------ | ---------------------------------- | ----------------------------------------------- |
| Speed        | Slow (services must be running)    | Fast (no network calls)                         |
| Reliability  | Flaky (environment issues, timing) | Stable                                          |
| Feedback     | Late (full pipeline)               | Early (per-commit)                              |
| Blast radius | Hard to pinpoint failures          | Pinpoints the exact interaction                 |
| Cross-team   | Requires coordination to run       | Async — consumer and provider run independently |

### Pact vs Schema Validation (JSON Schema, OpenAPI)

Schema validation only checks structure — it doesn't verify that the provider _actually_ returns data matching the schema, and it doesn't tell you which consumers depend on which fields.

Pact (consumer-driven) goes further:

- The consumer declares what it **actually uses** (not the full schema)
- The provider verifies it can satisfy those specific needs
- Results are tracked over time in the Pact Broker
- can-i-deploy prevents deploying incompatible versions

### Pact vs Spring Cloud Contract

|                 | Pact                                                    | Spring Cloud Contract              |
| --------------- | ------------------------------------------------------- | ---------------------------------- |
| Language        | Multi-language (JS, Java, Go, Ruby, .NET, Python, PHP…) | JVM-centric                        |
| Contract origin | Consumer-driven                                         | Can be consumer or provider-driven |
| Spec storage    | Pact Broker                                             | Git                                |
| Ecosystem       | Open source, PactFlow                                   | Spring ecosystem                   |

### Pact vs Postman/Newman

Postman tests hit a real running service. Pact tests don't need the provider running during consumer tests. Pact tests are also bi-directional — both consumer and provider run tests, and results are tracked.

---

## Common Questions from Skeptics

> Source: https://docs.pact.io/faq/convinceme.md

**"We already have E2E tests — why do we need Pact?"**

E2E tests verify that a particular combination of versions work together. They don't tell you _which_ version of each service is compatible with _which_ version of others. Pact gives you a compatibility matrix you can query before deploying any version to any environment.

**"The provider team won't commit to running pact verification."**

Without provider verification, consumer tests are just documentation of assumptions. The value of Pact is in the provider verification step. If you can't get provider buy-in, start with BDCT (Bi-Directional Contract Testing) — the provider publishes an OpenAPI spec, and PactFlow verifies automatically without requiring provider team engagement. See `references/bdct.md`.

**"We use a public/third-party API so we can't control the provider."**

Pact tests can still serve as documentation of your assumptions about the third-party API. You won't get provider verification, but you get a test suite that catches breaking changes when the third-party API changes in ways you didn't expect.

**"Our API changes too quickly for contract tests to be worth it."**

A rapidly changing API is exactly when contract tests are most valuable. Without them, you discover incompatibilities late in integration testing or in production. Pact surfaces them at commit time.


path: references/dsl.php.md
content: While you already know this, here is a reminder of the Pact-PHP classes and methods you will need to use to create a Pact test in PHP (having omitted deprecated, unadvised and less important methods):

File: /src/PhpPact/Standalone/MockService/MockServerConfig.php
```
namespace PhpPact\Standalone\MockService;

class MockServerConfig
  public function __construct()
  public function getConsumer(): string
  public function setConsumer(string $consumer): self
  public function getProvider(): string
  public function setProvider(string $provider): self
  public function getHost(): string
  public function setHost(string $host): self
  public function getPort(): int
  public function setPort(int $port): self
  public function getPactDir(): string
  public function setPactDir(string $pactDir): self
  public function getLogDir(): string
  public function setLogDir(string $logDir): self
  public function getLogLevel(): string
  public function setLogLevel(string $logLevel): self
  public function getCors(): bool
  public function setCors(bool $cors): self
  public function getSpec(): int
  public function setSpec(int $spec): self
  public function getConsumerVersion(): string
  public function setConsumerVersion(string $consumerVersion): self
```

File: /src/PhpPact/Consumer/InteractionBuilder.php
```
namespace PhpPact\Consumer;

class InteractionBuilder implements BuilderInterface
  public function __construct(MockServerEnvConfig $config);
  public function given(string $state, array $params = [], bool $overwrite = false): BuilderInterface;
  public function uponReceiving(string $description): BuilderInterface;
  public function with(array $request): BuilderInterface;
  public function willRespondWith(array $response): BuilderInterface;
  public function build(): bool;
  public function getInteraction(): Interaction;
```

File: /src/PhpPact/Config/Config.php
```
namespace PhpPact\Config;

class Config implements ConfigInterface
    public function __construct(string $consumer, string $provider);
    public function getPactDir(): string;
    public function setPactDir(string $pactDir): self;
    public function getLogDir(): string;
    public function setLogDir(string $logDir): self;
    public function getLogLevel(): string;
    public function setLogLevel(string $logLevel): self;
    public function getSpecificationVersion(): string;
    public function setSpecificationVersion(string $specificationVersion): self;
    public function getConsumer(): string;
    public function getProvider(): string;
```

File: /src/PhpPact/Consumer/Matcher/Matcher.php
```
namespace PhpPact\Consumer\Matcher;

class Matcher
  public static function regex(string $value, string $regex): array;
  public static function like($value): array;
  public static function eachLike($value, int $min = 1): array;
  public static function integer(int $value): array;
  public static function decimal(float $value): array;
  public static function boolean(bool $value): array;
  public static function timestamp(string $format = null, string $timestamp = null): array;
  public static function date(string $format = null, string $date = null): array;
  public static function time(string $format = null, string $time = null): array;
```

File: /src/PhpPact/Consumer/Model/ConsumerRequest.php
```
namespace PhpPact\Consumer\Model;

class ConsumerRequest
  public function setMethod(string $method): self;
  public function getMethod(): string;
  public function setPath(string $path): self;
  public function getPath(): string;
  public function setQuery(string $query): self;
  public function getQuery(): string;
  public function setHeaders(array $headers): self;
  public function getHeaders(): array;
  public function setBody($body): self;
  public function getBody();
```

File: /src/PhpPact/Consumer/Model/ProviderResponse.php
```
namespace PhpPact\Consumer\Model;

class ProviderResponse
  public function setStatus(int $status): self;
  public function getStatus(): int;
  public function setHeaders(array $headers): self;
  public function getHeaders(): array;
  public function setBody($body): self;
  public function getBody();
```

File: /src/PhpPact/Consumer/Model/Interaction.php
```
namespace PhpPact\Consumer\Model;

class Interaction
  public function setDescription(string $description): self;
  public function getDescription(): string;
  public function setProviderState(string $providerState): self;
  public function getProviderState(): string;
  public function setRequest(ConsumerRequest $request): self;
  public function getRequest(): ConsumerRequest;
  public function setResponse(ProviderResponse $response): self;
  public function getResponse(): ProviderResponse;
```

File: /src/PhpPact/Consumer/Matcher/Enum/HttpStatus.php
```
namespace PhpPact\Consumer\Matcher\Enum;

enum HttpStatus: int
    case INFORMATION = 'info';
    case SUCCESS = 'success';
    case REDIRECT = 'redirect';
    case CLIENT_ERROR = 'clientError';
    case SERVER_ERROR = 'serverError';
    case NON_ERROR = 'nonError';
    case ERROR = 'error';

    public function range(): Range
```

I'll also include a summary of the generator and matcher classes (omitting the file location and namespace for brevity):

```
class ExpressionFormatter
  public function format(string $expression): string;
class JsonFormatter
	public function format(array $data): string;
class Date
  public function generate(string $format = 'Y-m-d'): string;
class DateTime
	public function generate(string $format = ‘Y-m-d\TH:i:sP’): string;
class MockServerURL
  public function generate(): string;
class ProviderState
	public function generate(string $state): string;
class RandomBoolean
  public function generate(): bool;
class RandomDecimal
	public function generate(): float;
class RandomHexadecimal
  public function generate(int $length = 8): string;
class RandomInt
	public function generate(int $min = 0, int $max = PHP_INT_MAX): int;
class RandomString
  public function generate(int $length = 8): string;
class Regex
	public function generate(string $pattern): string;
class Time
  public function generate(string $format = 'H:i:s'): string;
class Uuid
  public function generate(): string;
class ArrayContains
  public function match(array $expected, array $actual): bool;
class Boolean
  public function match(bool $expected, $actual): bool;
class ContentType
  public function match(string $expected, string $actual): bool;
class Date
  public function match(string $expected, string $actual): bool;
class DateTime
  public function match(string $expected, string $actual): bool;
class Decimal
  public function match(float $expected, $actual): bool;
class EachKey
  public function match(array $expected, array $actual): bool;
class EachValue
  public function match(array $expected, array $actual): bool;
class Equality
  public function match($expected, $actual): bool;
class Includes
  public function match(string $expected, string $actual): bool;
class Integer
  public function match(int $expected, $actual): bool;
class MatchAll
  public function match(array $matchers, $actual): bool;
class MatchingField
  public function match($expected, $actual): bool;
class Max
  public function match($expected, $actual): bool;
class MaxType
  public function match($expected, $actual): bool;
class Min
  public function match($expected, $actual): bool;
class MinMaxType
  public function match($expected, $actual): bool;
class MinType
  public function match($expected, $actual): bool;
class NotEmpty
  public function match($actual): bool;
class NullValue
  public function match($actual): bool;
class Number
  public function match($expected, $actual): bool;
class Regex
  public function match(string $pattern, string $actual): bool;
class Semver
  public function match(string $expected, string $actual): bool;
class StatusCode
  public function match(int $expected, int $actual): bool;
```


path: references/pact-concepts.md
content: # Pact Core Concepts

> Sources: https://docs.pact.io/getting_started/terminology.md, https://docs.pact.io/getting_started/how_pact_works.md, https://docs.pact.io/getting_started/provider_states.md

---

## Terminology

### Service Consumer

For HTTP: An application that initiates an HTTP request to another application (the service provider). The consumer is always the initiator — regardless of whether the request is a GET or PUT/POST/PATCH.

For messages: An application which reads a message or data structure created by another application.

### Service Provider

For HTTP: A server that responds to an HTTP request from another application. A service provider may have one or more HTTP endpoints, and should be thought of as the "deployable unit" — endpoints that get deployed together should be considered part of the same provider.

For messages: An application that creates a message or data structure for another application to read.

### Interaction

A request and response pair. Each interaction has a description and one or more provider states. An HTTP pact consists of a collection of interactions.

### Pact file

A JSON file containing serialised interactions (requests and responses) or messages defined in the consumer tests. This is the Contract. A pact defines: consumer name, provider name, a collection of interactions or messages, and the pact specification version.

### Pact verification

For HTTP: The requests contained in a pact file are replayed against the provider code, and the responses returned are checked to ensure they match those expected in the pact file.

For messages: A piece of code on the provider is executed to generate a message for a given description, and the generated message is checked to ensure it matches what the pact file expects.

### Provider state

On the consumer side: A name describing a "state" (like a fixture) that the provider should be in when a given request is replayed — e.g. "when user John Doe exists" or "when user John Doe has a bank account". These allow the same endpoint to be tested under different scenarios.

On the provider side: When pact verification is executed, the provider state name is used to identify the setup code block to run before the request is executed.

### Pact specification

The [Pact Specification](https://github.com/pact-foundation/pact-specification) governs the structure of generated pact files for interoperability between languages. Uses semantic versioning. Current version: V4. See https://docs.pact.io/roadmap/feature_support for language support.

### Pact Broker

A permanently running, externally hosted service with an API and UI that allows contract testing to be integrated into a CI/CD pipeline. Open source project.

### PactFlow

A commercial offering of the Pact Broker which adds features required to use Pact at scale. Managed by SmartBear.

### The Matrix

A table showing the compatibility status of each consumer version and provider version, as determined by contract verification results. Feature of Pact Broker and PactFlow.

### Can-I-Deploy

A command line tool (also exposed as an MCP tool via `contract-testing_can_i_deploy`) that uses the Matrix to determine whether an application version is safe to deploy to a particular environment. It checks that there is a successful verification result between the application being deployed and the currently deployed version of each integrated application in that environment.

### Pacticipant

The term used in the Pact Broker/PactFlow API for "an application that participates in a pact". Both consumers and providers are pacticipants.

---

## How Pact Works

### Consumer testing

Consumer Pact tests operate on each interaction to answer: "assuming the provider returns the expected response for this request, does the consumer code correctly generate the request and handle the expected response?"

Process:

1. Using the Pact DSL, the expected request and response are registered with the mock service.
2. The consumer test code fires a real request to a mock provider (created by the Pact framework).
3. The mock provider compares the actual request with the expected request, and emits the expected response if comparison is successful.
4. The consumer test code confirms that the response was correctly understood.

Each interaction is independent — no context is maintained between interactions. Provider states handle the need for preconditions.

Once all interactions are tested, the Pact framework generates a pact file describing each interaction.

### Provider verification

In provider verification, each request from the pact file is sent to the provider, and the actual response is compared with the minimal expected response described in the consumer test.

Provider verification passes if each request generates a response that contains **at least** the data described in the minimal expected response (additional fields in the response are allowed).

The Pact framework calls provider state setup code before each interaction to put the provider in the right state.

### Message Pact (non-HTTP)

For async systems (Kafka, RabbitMQ, SNS, SQS, etc.):

- **Consumer side**: Test that the consumer can handle a given message payload. The Pact test targets the "Port" — the code that handles the domain payload, not the adapter that talks to the queue technology.
- **Provider side**: Test that the "Port" code can produce the correct message structure.

Pact abstracts away the queue technology and focuses on the message payload. The intermediary (queue/topic/bus) is replaced by the Pact framework during testing.

---

## Provider States — Deep Dive

> Source: https://docs.pact.io/getting_started/provider_states.md and https://docs.pact.io/provider/using_provider_states_effectively.md

A provider state is the **necessary precondition** for a particular test scenario to run. Think of it as the `Given` clause in Cucumber BDD.

Each interaction in a pact is verified in isolation, with no context from previous interactions. Provider states allow you to inject data directly into the data source before the interaction is run.

**What provider states can do:**

- Set up data records that must exist (e.g. "a user with id 123 exists")
- Stub downstream systems
- Return error responses that are hard to cause normally (e.g. 500 errors)
- Allow the same endpoint to be tested with different expected responses

**Keep in mind:** A provider state is about the state of the _provider_ (what data is there, how it will handle a response) — not about the state of the consumer or what is in the request.

### Avoiding false positives with provider states

The classic failure mode: the consumer declares `/search-alligators?name=Mary` but the correct param is `?firstname=Mary`. The provider ignores the wrong param and returns data anyway — the test passes, but production fails.

**Solution 1**: Have the provider echo the query params it actually used in the response body:

```json
{ "query": { "name": ["Mary"] }, "alligators": [...] }
```

If the wrong param was used, `query` would be empty and the pact verification would fail correctly.

**Solution 2**: Set up the provider state with multiple data points so a wrong param can't accidentally match:

- Provider state: "an alligator named Mary exists AND an alligator named John exists"
- The pact expects exactly ONE alligator named Mary
- If the wrong param returns all alligators, the count assertion fails

### Fetching existing provider states

Before writing new consumer tests, always check existing provider states with:

```
contract-testing_get_provider_states
  provider: "YourProviderName"
```

This helps reuse existing state names and avoids duplication across consumer tests.


path: references/dsl.golang.md
content: While you already know this, here is a reminder of the key Pact Go interfaces, types, structs and methods you will need to use to create a Pact test in Golang (having omitted deprecated and unadvised methods):

File: ./models/pact_file.go
```
package models

type SpecificationVersion string

const (
	V2 SpecificationVersion = "2.0.0"
	V3 = "3.0.0"
	V4 = "4.0.0"
)
```

File: ./models/provider_state.go
```
package models

type ProviderState struct {
	Name string `json:"name"`
	Parameters map[string]interface{} `json:"params,omitempty"`
}

type ProviderStateResponse map[string]interface{}
type StateHandler func(setup bool, state ProviderState) (ProviderStateResponse, error)
type StateHandlers map[string]StateHandler
```

File: ./log/log.go
```
package log

func SetLogLevel(level logutils.LogLevel) error
func LogLevel() logutils.LogLevel
```

File: ./matchers/matcher.go
```
package matchers

func EachLike(content interface{}, minRequired int) Matcher
var ArrayMinLike func(content interface{}, minRequired int) Matcher
func Like(content interface{}) Matcher
func Term(generate string, matcher string) Matcher
func HexValue() Matcher
func Identifier() Matcher
func IPAddress() Matcher
var IPv4Address func() Matcher
func IPv6Address() Matcher
```

File: ./matchers/matcher_v3.go
```
package matchers

func Decimal(example float64) Matcher
func Integer(example int) Matcher
type Null struct{}
func (n Null) GetValue() interface{}
func (n Null) MarshalJSON() ([]byte, error)
func Equality(content interface{}) Matcher
func Includes(content string) Matcher
func FromProviderState(expression, example string) Matcher
func EachKeyLike(key string, template interface{}) Matcher
func ArrayContaining(variants []interface{}) Matcher
func ArrayMinMaxLike(content interface{}, min int, max int) Matcher
func ArrayMaxLike(content interface{}, max int) Matcher
func DateGenerated(example string, format string) Matcher
func TimeGenerated(example string, format string) Matcher
func DateTimeGenerated(example string, format string) Matcher
```

File: ./consumer/interaction.go
```
package consumer

type Interaction struct {
	interaction          interface{}
	specificationVersion models.SpecificationVersion
}

func (i *Interaction) WithCompleteRequest(request Request) *Interaction
func (i *Interaction) WithCompleteResponse(response Response) *Interaction
```

File: ./consumer/http_v3.go
```
package consumer

type V3HTTPMockProvider struct {
	*httpMockProvider
}

func NewV3Pact(config MockHTTPProviderConfig) (*V3HTTPMockProvider, error)
func (p *V3HTTPMockProvider) AddInteraction() *V3UnconfiguredInteraction
type V3UnconfiguredInteraction struct {
	interaction *Interaction
	provider    *V3HTTPMockProvider
}
func (i *V3UnconfiguredInteraction) Given(state string) *V3UnconfiguredInteraction
func (i *V3UnconfiguredInteraction) GivenWithParameter(state models.ProviderState) *V3UnconfiguredInteraction
func (i *V3UnconfiguredInteraction) UponReceiving(description string) *V3UnconfiguredInteraction
func (i *V3UnconfiguredInteraction) WithCompleteRequest(request Request) *V3InteractionWithCompleteRequest
func (i *V3UnconfiguredInteraction) WithRequest(method Method, path string, builders ...V3RequestBuilderFunc) *V3InteractionWithRequest
func (i *V3UnconfiguredInteraction) WithRequestPathMatcher(method Method, path matchers.Matcher, builders ...V3RequestBuilderFunc) *V3InteractionWithRequest
type V3InteractionWithRequest struct {
	interaction *Interaction
	provider    *V3HTTPMockProvider
}
func (i *V3InteractionWithRequest) WillRespondWith(status int, builders ...V3ResponseBuilderFunc) *V3InteractionWithResponse
type V3InteractionWithCompleteRequest struct {}
func (i *V3InteractionWithCompleteRequest) WithCompleteResponse(response Response) *V3InteractionWithResponse
type V3InteractionWithResponse struct {}
type V3RequestBuilderFunc func(*V3RequestBuilder)
type V3RequestBuilder struct {}
func (i *V3RequestBuilder) Query(key string, values ...matchers.Matcher) *V3RequestBuilder
func (i *V3RequestBuilder) Header(key string, values ...matchers.Matcher) *V3RequestBuilder
func (i *V3RequestBuilder) Headers(headers matchers.HeadersMatcher) *V3RequestBuilder
func (i *V3RequestBuilder) JSONBody(body interface{}) *V3RequestBuilder
func (i *V3RequestBuilder) BinaryBody(body []byte) *V3RequestBuilder
func (i *V3RequestBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V3RequestBuilder
func (i *V3RequestBuilder) Body(contentType string, body []byte) *V3RequestBuilder
func (i *V3RequestBuilder) BodyMatch(body interface{}) *V3RequestBuilder
type V3ResponseBuilderFunc func(*V3ResponseBuilder)
type V3ResponseBuilder struct {}
func (i *V3ResponseBuilder) Header(key string, values ...matchers.Matcher) *V3ResponseBuilder
func (i *V3ResponseBuilder) Headers(headers matchers.HeadersMatcher) *V3ResponseBuilder
func (i *V3ResponseBuilder) JSONBody(body interface{}) *V3ResponseBuilder
func (i *V3ResponseBuilder) BinaryBody(body []byte) *V3ResponseBuilder
func (i *V3ResponseBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V3ResponseBuilder
```

File: ./consumer/response.go
```
package consumer

// Response is the default implementation of the Response interface.
type Response struct {
	Status  int
	Headers matchers.MapMatcher
	Body    interface{}
}
```

File: ./consumer/request.go
```
package consumer

type Request struct {
	Method  string
	Path    matchers.Matcher
	Query   matchers.MapMatcher
	Headers matchers.MapMatcher
	Body    interface{}
}
type Method string
```

File: ./consumer/http_v2.go
```
package consumer

type V2HTTPMockProvider struct {}

func NewV2Pact(config MockHTTPProviderConfig) (*V2HTTPMockProvider, error)
func (p *V2HTTPMockProvider) AddInteraction() *V2UnconfiguredInteraction
type V2UnconfiguredInteraction struct {}
func (i *V2UnconfiguredInteraction) Given(state string) *V2UnconfiguredInteraction
func (i *V2UnconfiguredInteraction) UponReceiving(description string) *V2UnconfiguredInteraction
func (i *V2UnconfiguredInteraction) WithCompleteRequest(request Request) *V2InteractionWithCompleteRequest
func (i *V2UnconfiguredInteraction) WithRequest(method Method, path string, builders ...V2RequestBuilderFunc) *V2InteractionWithRequest
func (i *V2UnconfiguredInteraction) WithRequestPathMatcher(method Method, path matchers.Matcher, builders ...V2RequestBuilderFunc) *V2InteractionWithRequest
type V2InteractionWithRequest struct {}
func (i *V2InteractionWithRequest) WillRespondWith(status int, builders ...V2ResponseBuilderFunc) *V2InteractionWithResponse
type V2InteractionWithCompleteRequest struct {}
func (i *V2InteractionWithCompleteRequest) WithCompleteResponse(response Response) *V2InteractionWithResponse
type V2InteractionWithResponse struct {}
type V2RequestBuilderFunc func(*V2RequestBuilder)
type V2RequestBuilder struct {}
func (i *V2RequestBuilder) Query(key string, values ...matchers.Matcher) *V2RequestBuilder
func (i *V2RequestBuilder) Header(key string, values ...matchers.Matcher) *V2RequestBuilder
func (i *V2RequestBuilder) Headers(headers matchers.HeadersMatcher) *V2RequestBuilder
func (i *V2RequestBuilder) JSONBody(body interface{}) *V2RequestBuilder
func (i *V2RequestBuilder) BinaryBody(body []byte) *V2RequestBuilder
func (i *V2RequestBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V2RequestBuilder
func (i *V2RequestBuilder) Body(contentType string, body []byte) *V2RequestBuilder
func (i *V2RequestBuilder) BodyMatch(body interface{}) *V2RequestBuilder
type V2ResponseBuilderFunc func(*V2ResponseBuilder)
type V2ResponseBuilder struct {}
func (i *V2ResponseBuilder) Header(key string, values ...matchers.Matcher) *V2ResponseBuilder
func (i *V2ResponseBuilder) Headers(headers matchers.HeadersMatcher) *V2ResponseBuilder
func (i *V2ResponseBuilder) JSONBody(body interface{}) *V2ResponseBuilder
func (i *V2ResponseBuilder) BinaryBody(body []byte) *V2ResponseBuilder
func (i *V2ResponseBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V2ResponseBuilder
func (i *V2ResponseBuilder) Body(contentType string, body []byte) *V2ResponseBuilder
func (i *V2ResponseBuilder) BodyMatch(body interface{}) *V2ResponseBuilder
```

File: ./consumer/http.go
```
package consumer

type MockHTTPProviderConfig struct {
	Consumer               string
	Provider               string
	LogDir                 string
	PactDir                string
	Host                   string
	AllowedMockServerPorts string
	Port                   int
	ClientTimeout          time.Duration
	TLS                    bool
}
type MockServerConfig struct {
	Port      int
	Host      string
	TLSConfig *tls.Config
}
```

File: ./consumer/http_v4.go
```
package consumer

type V4HTTPMockProvider struct {}
func NewV4Pact(config MockHTTPProviderConfig) (*V4HTTPMockProvider, error)
func (p *V4HTTPMockProvider) AddInteraction() *V4UnconfiguredInteraction
type V4UnconfiguredInteraction struct {}
func (i *V4UnconfiguredInteraction) Given(state string) *V4UnconfiguredInteraction
func (i *V4UnconfiguredInteraction) GivenWithParameter(state models.ProviderState) *V4UnconfiguredInteraction
func (i *V4UnconfiguredInteraction) UponReceiving(description string) *V4UnconfiguredInteraction
func (i *V4UnconfiguredInteraction) WithCompleteRequest(request Request) *V4InteractionWithCompleteRequest
func (i *V4UnconfiguredInteraction) WithRequest(method Method, path string, builders ...V4RequestBuilderFunc) *V4InteractionWithRequest
func (i *V4UnconfiguredInteraction) WithRequestPathMatcher(method Method, path matchers.Matcher, builders ...V4RequestBuilderFunc) *V4InteractionWithRequest
type V4InteractionWithRequest struct {}
func (i *V4InteractionWithRequest) WillRespondWith(status int, builders ...V4ResponseBuilderFunc) *V4InteractionWithResponse
type V4InteractionWithCompleteRequest struct {}
func (i *V4InteractionWithCompleteRequest) WithCompleteResponse(response Response) *V4InteractionWithResponse
type V4RequestBuilder struct {}
type V4RequestBuilderFunc func(*V4RequestBuilder)
func (i *V4RequestBuilder) Query(key string, values ...matchers.Matcher) *V4RequestBuilder
func (i *V4RequestBuilder) Header(key string, values ...matchers.Matcher) *V4RequestBuilder
func (i *V4RequestBuilder) Headers(headers matchers.HeadersMatcher) *V4RequestBuilder
func (i *V4RequestBuilder) JSONBody(body interface{}) *V4RequestBuilder
func (i *V4RequestBuilder) BinaryBody(body []byte) *V4RequestBuilder
func (i *V4RequestBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V4RequestBuilder
func (i *V4RequestBuilder) Body(contentType string, body []byte) *V4RequestBuilder
func (i *V4RequestBuilder) BodyMatch(body interface{}) *V4RequestBuilder
type V4ResponseBuilder struct {}
type V4ResponseBuilderFunc func(*V4ResponseBuilder)
type V4InteractionWithResponse struct {}
func (i *V4ResponseBuilder) Header(key string, values ...matchers.Matcher) *V4ResponseBuilder
func (i *V4ResponseBuilder) Headers(headers matchers.HeadersMatcher) *V4ResponseBuilder
func (i *V4ResponseBuilder) JSONBody(body interface{}) *V4ResponseBuilder
func (i *V4ResponseBuilder) BinaryBody(body []byte) *V4ResponseBuilder
func (i *V4ResponseBuilder) MultipartBody(contentType string, filename string, mimePartName string) *V4ResponseBuilder
```


path: references/dsl.kotlin.md
content: While you already know this, here is a reminder of the Pact-JVM classes and methods you will need to use to create a Pact test in Kotlin (having omitted deprecated and unadvised methods). Pact JVM doesn't provide a Kotlin-specific DSL, so you will need to account for this when creating Kotlin tests:

```java
class LambdaDslJsonBody:
    fn build() -> DslPart
class LambdaDsl:
    fn newJsonArray(array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinLike(size: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMaxLike(size: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinMaxLike(minSize: Integer, maxSize: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayUnordered(array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinUnordered(size: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMaxUnordered(size: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinMaxUnordered(minSize: int, maxSize: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonBody(array: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonBody
public interface BodyBuilder:
    fn getMatchers() -> MatchingRuleCategory
    fn getGenerators() -> Generators
    fn getContentType() -> ContentType
    fn buildBody() -> byte[]
    fn getHeaderMatchers() -> MatchingRuleCategory
class LambdaDslObject:
    fn getPactDslObject() -> PactDslJsonBody
    fn stringValue(name: String, value: String) -> LambdaDslObject
    fn stringType(name: String, example: String) -> LambdaDslObject
    fn stringType(name: String) -> LambdaDslObject
    fn stringType(names: String...) -> LambdaDslObject
    fn stringMatcher(name: String, regex: String) -> LambdaDslObject
    fn stringMatcher(name: String, regex: String, example: String) -> LambdaDslObject
    fn numberValue(name: String, value: Number) -> LambdaDslObject
    fn numberType(name: String, example: Number) -> LambdaDslObject
    fn numberType(names: String...) -> LambdaDslObject
    fn integerType(name: String, example: Integer) -> LambdaDslObject
    fn integerType(names: String...) -> LambdaDslObject
    fn decimalType(name: String, example: BigDecimal) -> LambdaDslObject
    fn decimalType(name: String, example: Double) -> LambdaDslObject
    fn decimalType(names: String...) -> LambdaDslObject
    fn numberMatching(name: String, regex: String, example: Number) -> LambdaDslObject
    fn decimalMatching(name: String, regex: String, example: Double) -> LambdaDslObject
    fn integerMatching(name: String, regex: String, example: Integer) -> LambdaDslObject
    fn booleanValue(name: String, value: Boolean) -> LambdaDslObject
    fn booleanType(name: String, example: Boolean) -> LambdaDslObject
    fn booleanType(names: String...) -> LambdaDslObject
    fn id() -> LambdaDslObject
    fn id(name: String) -> LambdaDslObject
    fn id(name: String, example: Long) -> LambdaDslObject
    fn uuid(name: String) -> LambdaDslObject
    fn uuid(name: String, example: UUID) -> LambdaDslObject
    fn date() -> LambdaDslObject
    fn date(name: String) -> LambdaDslObject
    fn date(name: String, format: String) -> LambdaDslObject
    fn date(name: String, format: String, example: Date) -> LambdaDslObject
    fn date(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn date(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn date(name: String, format: String, example: LocalDate) -> LambdaDslObject
    fn time() -> LambdaDslObject
    fn time(name: String) -> LambdaDslObject
    fn time(name: String, format: String) -> LambdaDslObject
    fn time(name: String, format: String, example: Date) -> LambdaDslObject
    fn time(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn time(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn datetime(name: String, format: String) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Date) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Instant) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn datetime(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn ipV4Address(name: String) -> LambdaDslObject
    fn valueFromProviderState(name: String, expression: String, example: Object) -> LambdaDslObject
    fn and(name: String, value: Object, rules: MatchingRule...) -> LambdaDslObject
    fn or(name: String, value: Object, rules: MatchingRule...) -> LambdaDslObject
    fn array(name: String, array: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn object(name: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, value: PactDslJsonRootValue) -> LambdaDslObject
    fn eachLike(name: String, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn nullValue(fieldName: String) -> LambdaDslObject
    fn eachArrayLike(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayLike(name: String, numberExamples: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMaxLike(name: String, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMaxLike(name: String, numberExamples: Int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinLike(name: String, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinLike(name: String, numberExamples: Int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinMaxLike(name: String, minSize: Integer, maxSize: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinMaxLike(name: String, minSize: Integer, maxSize: Integer, numberExamples: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachKeyMappedToAnArrayLike(exampleKey: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachKeyLike(exampleKey: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachKeyLike(exampleKey: String, value: PactDslJsonRootValue) -> LambdaDslObject
    fn dateExpression(name: String, expression: String) -> LambdaDslObject
    fn dateExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn timeExpression(name: String, expression: String) -> LambdaDslObject
    fn timeExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn datetimeExpression(name: String, expression: String) -> LambdaDslObject
    fn datetimeExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn unorderedArray(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMinArray(name: String, size: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMaxArray(name: String, size: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMinMaxArray(name: String, minSize: Int, maxSize: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn matchUrl(name: String, basePath: String, pathFragments: Object...) -> LambdaDslObject
    fn matchUrl2(name: String, pathFragments: Object...) -> LambdaDslObject
    fn arrayContaining(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
class LambdaDslJsonArray:
    fn getPactDslJsonArray() -> PactDslJsonArray
    fn object(o: Consumer<LambdaDslObject>) -> LambdaDslJsonArray
    fn array(a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedArray(a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMinArray(size: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMaxArray(size: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMinMaxArray(minSize: int, maxSize: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn stringValue(value: String) -> LambdaDslJsonArray
    fn stringType(example: String) -> LambdaDslJsonArray
    fn stringMatcher(regex: String, example: String) -> LambdaDslJsonArray
    fn numberValue(value: Number) -> LambdaDslJsonArray
    fn numberType(example: Number) -> LambdaDslJsonArray
    fn integerType() -> LambdaDslJsonArray
    fn integerType(example: Long) -> LambdaDslJsonArray
    fn decimalType() -> LambdaDslJsonArray
    fn decimalType(example: BigDecimal) -> LambdaDslJsonArray
    fn decimalType(example: Double) -> LambdaDslJsonArray
    fn numberMatching(regex: String, example: Number) -> LambdaDslJsonArray
    fn decimalMatching(regex: String, example: Double) -> LambdaDslJsonArray
    fn integerMatching(regex: String, example: Integer) -> LambdaDslJsonArray
    fn booleanValue(value: Boolean) -> LambdaDslJsonArray
    fn booleanType(example: Boolean) -> LambdaDslJsonArray
    fn date() -> LambdaDslJsonArray
    fn date(format: String) -> LambdaDslJsonArray
    fn date(format: String, example: Date) -> LambdaDslJsonArray
    fn time() -> LambdaDslJsonArray
    fn time(format: String) -> LambdaDslJsonArray
    fn time(format: String, example: Date) -> LambdaDslJsonArray
    fn id() -> LambdaDslJsonArray
    fn id(example: Long) -> LambdaDslJsonArray
    fn uuid() -> LambdaDslJsonArray
    fn uuid(example: String) -> LambdaDslJsonArray
    fn hexValue() -> LambdaDslJsonArray
    fn hexValue(example: String) -> LambdaDslJsonArray
    fn ipV4Address() -> LambdaDslJsonArray
    fn and(value: Object, rules: MatchingRule...) -> LambdaDslJsonArray
    fn or(value: Object, rules: MatchingRule...) -> LambdaDslJsonArray
    fn eachLike(nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn eachLike(value: PactDslJsonRootValue) -> LambdaDslJsonArray
    fn eachLike(value: PactDslJsonRootValue, numberExamples: int) -> LambdaDslJsonArray
    fn eachLike(numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minArrayLike(size: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minArrayLike(size: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn maxArrayLike(size: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn maxArrayLike(size: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minMaxArrayLike(minSize: Integer, maxSize: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minMaxArrayLike(minSize: Integer, maxSize: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn nullValue() -> LambdaDslJsonArray
    fn eachArrayLike(nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayLike(numberExamples: int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMaxLike(size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMaxLike(numberExamples: int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinLike(size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinLike(numberExamples: int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinMaxLike(minSize: Integer, maxSize: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinMaxLike(minSize: Integer, maxSize: Integer, numberExamples: int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn dateExpression(expression: String) -> LambdaDslJsonArray
    fn dateExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn timeExpression(expression: String) -> LambdaDslJsonArray
    fn timeExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn datetimeExpression(expression: String) -> LambdaDslJsonArray
    fn datetimeExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn build() -> DslPart
class QuoteUtil:
    fn convert(text: String) -> String
abstract class DslPart:
    fn putObjectPrivate(obj: DslPart) -> Unit
    fn putArrayPrivate(obj: DslPart) -> Unit
    fn array(name: String) -> PactDslJsonArray
    fn array() -> PactDslJsonArray
    fn unorderedArray(name: String) -> PactDslJsonArray
    fn unorderedArray() -> PactDslJsonArray
    fn unorderedMinArray(name: String, size: Int) -> PactDslJsonArray
    fn unorderedMinArray(size: Int) -> PactDslJsonArray
    fn unorderedMaxArray(name: String, size: Int) -> PactDslJsonArray
    fn unorderedMaxArray(size: Int) -> PactDslJsonArray
    fn unorderedMinMaxArray(name: String, minSize: Int, maxSize: Int) -> PactDslJsonArray
    fn unorderedMinMaxArray(minSize: Int, maxSize: Int) -> PactDslJsonArray
    fn closeArray() -> DslPart?
    fn eachLike(name: String) -> PactDslJsonBody
    fn eachLike(name: String, obj: DslPart) -> PactDslJsonBody
    fn eachLike() -> PactDslJsonBody
    fn eachLike(obj: DslPart) -> PactDslJsonArray
    fn eachLike(name: String, numberExamples: Int) -> PactDslJsonBody
    fn eachLike(numberExamples: Int) -> PactDslJsonBody
    fn minArrayLike(name: String, size: Int) -> PactDslJsonBody
    fn minArrayLike(size: Int) -> PactDslJsonBody
    fn minArrayLike(name: String, size: Int, obj: DslPart) -> PactDslJsonBody
    fn minArrayLike(size: Int, obj: DslPart) -> PactDslJsonArray
    fn minArrayLike(name: String, size: Int, numberExamples: Int) -> PactDslJsonBody
    fn minArrayLike(size: Int, numberExamples: Int) -> PactDslJsonBody
    fn maxArrayLike(name: String, size: Int) -> PactDslJsonBody
    fn maxArrayLike(size: Int) -> PactDsl
```

You want to encourage your organisation to use the more modern `LambdaDsl` classes and methods when writing Pact tests. So instead of:

```kotlin
val requestBody = PactDslJsonBody()
    .stringValue("title", "Pact is awesome!")
    .numberType("userId", 42);
```

use:

```kotlin
val requestBody = newJsonBody((o) -> {
            o.stringType("title", "Pact is awesome!");
            o.numberType("userId", 42);
        }).build();
```


path: references/pact-consumer.md
content: # Pact Consumer Guide

> Sources: https://docs.pact.io/consumer.md, https://docs.pact.io/getting_started/matching.md, https://docs.pact.io/consumer/recommended_configuration.md

---

## The Golden Rule for Writing Consumer Tests

When authoring consumer pact tests: **start with the goal of writing a good set of unit tests for your API client.** An important side-effect of doing this with Pact is that you also produce an API contract you can use for contract testing.

The art of writing good consumer Pact tests is mostly about knowing what **not** to test.

### What to test

- Bugs in how the consumer **creates requests** or **handles responses**
- **Misunderstandings** about how the provider will respond

### What NOT to test

- Bugs in the provider (belongs in provider tests)
- Business logic unrelated to the HTTP exchange
- Side effects on the provider (belongs in functional tests)

**Rule of thumb**: If you don't include a scenario, what bug in the consumer or what misunderstanding about the provider might be missed? If the answer is none, don't include it.

---

## Only Test What Would Affect the Consumer If It Changed

Avoid asserting on general business rules you know about the provider (e.g. "customer ID format is `[A-Z]{3}-\d{3}`"). Only assert on things that would break the consumer if they changed (e.g. "a link must start with `http` because the consumer builds absolute URLs from it").

This allows the provider to evolve without false alerts from overly strict pact verification tests.

---

## Choosing the Right Matching Type

**For requests (consumer side)**:

- Exact matching is usually appropriate for unit-level tests — you control both the expectation and the actual request.
- If using Pact for broader tests, use type-based matching and regular expressions to avoid brittleness.

**For responses**:

- Loose (type-based) matching is generally recommended. Ask: "if I made this looser/tighter what bugs would I miss/prevent?"
- Use exact matching only when the exact value matters to the consumer.
- Avoid strict matchers on fields where you're parsing data out that the API should be providing separately.

---

## Matching Features

> Source: https://docs.pact.io/getting_started/matching.md

### Regular expressions

Use when a value must match a pattern but the exact value doesn't matter:

```ruby
# Ruby DSL example
body: {
  name: "Mary",
  dateOfBirth: Pact.term(
    generate: "02/11/2013",
    matcher: /\d{2}\/\d{2}\/\d{4}/)
}
```

- Consumer test: mock returns the `generate` value
- Provider verification: checks that the actual value matches the `matcher` regex

### Type matching (SomethingLike)

Use when you care about the type but not the exact value:

```ruby
body: Pact.like(
  name: "Mary",   # any String
  age: 73         # any Integer
)
```

Wrap only specific fields to mix exact and type matching:

```ruby
body: {
  name: "Mary",        # exact match
  age: Pact.like(73)   # any Integer
}
```

### Flexible-length arrays (EachLike)

Use when you expect an array of items matching a certain structure, but don't care about the exact count:

```ruby
body: {
  alligators: Pact.each_like({ name: "Mary", age: 3 })
  # Any array with at least one item matching this structure
}
```

You can specify the minimum count:

```ruby
Pact.each_like({ name: "Mary" }, minimum: 2)
```

### Query params

Specified as a string (exact match) or a hash (flexible order):

```ruby
# Exact string — order matters
query: "name=Mary+Jane&age=8"

# Hash — order doesn't matter, can embed Pact::Term
query: {
  name: 'Mary Jane',
  age: '8',
  children: ['Betty', 'Sue']  # array — order enforced
}
```

### Matching gotchas (Postel's Law)

> Source: https://docs.pact.io/getting_started/matching/gotchas.md

Pact follows Postel's Law: be conservative in what you send, be liberal in what you accept.

- **Extra keys in the response are allowed** — pact verification only checks the keys in the pact, not that the response contains _only_ those keys.
- **Extra keys in the request are NOT allowed** — the provider mock expects an exact request (unless you use matchers).
- **Type matching on arrays**: `EachLike` checks each element against the template, but does not enforce the array length unless `minimum` is specified.
- **Regex on strings only** — regular expressions can only be applied to String values.

---

## Recommended Consumer Configuration

> Source: https://docs.pact.io/consumer/recommended_configuration.md

### Publishing pacts

Always publish pacts with:

1. **A branch name** (the git branch the consumer is on) — required for branch-based can-i-deploy
2. **A version number** (use the git SHA for accuracy)
3. **A build URL** (optional but helpful for traceability)

```bash
# Using pact-broker CLI
pact-broker publish ./pacts \
  --pacticipant YourConsumer \
  --version $GIT_COMMIT \
  --branch $GIT_BRANCH \
  --broker-base-url $PACT_BROKER_BASE_URL
```

Or use the MCP tool:

```
contract-testing_publish_consumer_contracts
  pacticipantName: "YourConsumer"
  pacticipantVersionNumber: "<git-sha>"
  branch: "<git-branch>"
  contracts: [...]
  buildUrl: "<ci-build-url>"
```

### After deploying to an environment

Record the deployment so can-i-deploy has accurate state:

```
contract-testing_record_deployment
  pacticipantName: "YourConsumer"
  versionNumber: "<git-sha>"
  environmentId: "<environment-uuid>"
```

### Using tags (legacy — prefer branches)

Tags were the original way to track branches and deployments. They are still supported but branches + environments are now preferred. If you must use tags:

- Tag with branch name when publishing (e.g. `main`, `feat/new-api`)
- Tag with environment name when deploying (e.g. `production`, `staging`)

---

## Avoiding Common Pitfalls

### Don't use Pact for UI tests

If you use Pact for UI tests you will end up with:

- Very hard to debug tests with multiple simultaneous mock interactions
- Multiple redundant calls with slight variations that increase maintenance

Instead: use the generated pact file itself (or shared fixtures) to provide HTTP stubs for UI tests.

### Don't test functional logic

```
# BAD: testing provider business logic
"when a user has a premium subscription, the discount should be 20%"

# GOOD: testing the contract
"given user 123 exists, GET /users/123 returns a user object with name (String) and discount (Number)"
```

### Test only the API client layer

Write Pact tests for the class/function responsible for making HTTP calls to the provider. Don't cover multiple layers of the consumer stack — it makes tests brittle and creates an explosion of interactions.

### Don't use random data in pacts

Random or dynamic data in pacts creates a new database row every time. Use stable, fixed example values and rely on matchers (regex, type) for flexibility. The mock server replays fixed `generate` values; the provider checks the pattern.


path: references/bdct.md
content: # Bi-Directional Contract Testing (BDCT)

BDCT is a PactFlow Cloud-only feature that removes the need for the provider to run consumer pact tests directly. Instead, the provider publishes its OpenAPI specification and the results of a self-verification tool (e.g. Dredd, Schemathesis, Postman). PactFlow performs the cross-contract comparison automatically.

## When to Use BDCT

- The provider team cannot or will not run consumer Pact tests in their pipeline
- The provider already has API spec-based testing (Dredd, Schemathesis, etc.)
- Migrating an existing REST API to contract testing with minimal provider-side changes
- Large organisations where consumer and provider teams are loosely coupled

## BDCT Workflow

```
Provider publishes OpenAPI spec + self-verification results
      ↓
contract-testing_publish_provider_contract
      ↓
PactFlow performs cross-contract verification automatically
      ↓
Consumer publishes pact (same as standard flow)
contract-testing_publish_consumer_contracts
      ↓
can-i-deploy checks BDCT cross-contract results
contract-testing_can_i_deploy
      ↓
Deploy + record
contract-testing_record_deployment
```

## Publishing a Provider Contract

```
contract-testing_publish_provider_contract
  providerName: "OrderService"
  pacticipantVersionNumber: "def5678"
  branch: "main"
  buildUrl: "https://ci.example.com/builds/99"
  contract:
    content: "<base64-encoded OpenAPI YAML or JSON>"
    contentType: "application/yaml"   # or application/json
    specification: "oas"
    selfVerificationResults:
      success: true
      verifier: "dredd"              # or schemathesis, postman, etc.
      # content and format are optional — include if you want results stored
```

The `selfVerificationResults.success` boolean is critical — PactFlow will not mark the provider as verified unless this is `true`.

## Investigating BDCT Failures

Start broad, then drill down:

### 1. Cross-contract results for a provider version

```
contract-testing_get_bdct_cross_contract_verification_results
  providerName: "OrderService"
  providerVersionNumber: "def5678"
```

This shows the overall pass/fail and which consumer pact interactions failed the spec comparison.

### 2. Consumer contracts that were compared

```
contract-testing_get_bdct_consumer_contracts
  providerName: "OrderService"
  providerVersionNumber: "def5678"
```

Lists all consumer pact files that PactFlow used in the cross-contract verification.

### 3. Provider self-verification results

```
contract-testing_get_bdct_provider_contract_verification_results
  providerName: "OrderService"
  providerVersionNumber: "def5678"
```

The output of the provider's own spec-verification tool (Dredd, Schemathesis, etc.). If `success: false`, the provider failed its own self-verification — the OpenAPI spec doesn't match the implementation.

### 4. Pinpointing a specific consumer-provider version pair

When you know which consumer version is failing:

```
# The cross-contract result for this exact pair
contract-testing_get_bdct_cross_contract_verification_results_by_consumer_version
  providerName: "OrderService"
  providerVersionNumber: "def5678"
  consumerName: "CheckoutApp"
  consumerVersionNumber: "abc1234"

# The consumer's pact that was compared
contract-testing_get_bdct_consumer_contract_by_consumer_version
  providerName: "OrderService"
  providerVersionNumber: "def5678"
  consumerName: "CheckoutApp"
  consumerVersionNumber: "abc1234"

# The provider's OpenAPI spec that was used
contract-testing_get_bdct_provider_contract_by_consumer_version
  providerName: "OrderService"
  providerVersionNumber: "def5678"
  consumerName: "CheckoutApp"
  consumerVersionNumber: "abc1234"
```

## BDCT vs Standard Pact: Key Differences

| Aspect                       | Standard Pact                               | BDCT                                               |
| ---------------------------- | ------------------------------------------- | -------------------------------------------------- |
| Provider runs consumer tests | Yes                                         | No                                                 |
| Provider publishes           | Verification results (via pact library)     | OpenAPI spec + self-verification results           |
| Cross-contract verification  | Done by provider test suite                 | Done by PactFlow automatically                     |
| Consumer side                | Same (publish pact, run can-i-deploy)       | Same                                               |
| Availability                 | Cloud + On-Prem + Open Source Broker        | PactFlow Cloud only                                |
| Best for                     | Tight teams with shared test infrastructure | Loosely coupled teams; existing spec-based testing |

## Common Issues

**Cross-contract verification fails even though provider self-verification passed** — The OpenAPI spec doesn't fully cover what the consumer expects. Common causes: the spec uses `additionalProperties: false` and the consumer sends extra fields; response body schema is too strict; missing response headers the consumer expects.

**`success: false` in self-verification results** — The provider's implementation doesn't match its own OpenAPI spec. The provider needs to fix either the implementation or the spec before BDCT can pass.

**Consumer pact publishes fine but can-i-deploy still fails** — Check `contract-testing_get_bdct_cross_contract_verification_results` for the specific provider version deployed in the target environment. The provider may need to re-publish a new version with an updated spec.


path: references/pact-messages.md
content: # Message Pact (Async & Event-Driven Testing)

> Sources: https://docs.pact.io/getting_started/how_pact_works.md, https://docs.pact.io/implementation_guides/javascript/docs/messages.md, https://docs.pact.io/recipes/kafka.md

---

## Overview

Modern distributed architectures use message queues (ActiveMQ, RabbitMQ, SNS, SQS, Kafka, Kinesis) and event-driven patterns. Pact supports these via **Message Pact**, which abstracts away the queueing technology and focuses on the messages themselves.

Pact does NOT know about specific queueing technology. It focuses on:

- The **structure and content** of messages (not the transport)
- Whether the consumer can **handle** a given message
- Whether the provider can **produce** the correct message structure

---

## How Message Pact Works

### Hexagonal Architecture Pattern

Split your code into:

- **Adapter** — the protocol-specific code (e.g. the AWS Lambda handler that receives SNS, or the Kafka consumer)
- **Port** — the domain code that handles the payload, unaware of the transport

The Pact test targets the **Port**, not the Adapter.

#### Consumer side

The Port is the function that receives the deserialized message and does something with it. Pact verifies that this function can handle the message structure the provider produces.

#### Provider (Producer) side

The Port is the function that creates the message payload. Pact verifies that this function produces the structure the consumer expects.

---

## Message Types

### Asynchronous Messages (Queue/Event-based)

Single messages sent one way: producer → queue → consumer.

Use cases: Kafka events, SNS notifications, SQS messages, EventBridge events.

### Synchronous Messages (Request/Response over non-HTTP)

Request/response pairs over non-HTTP transports.

Use cases: gRPC calls, WebSocket exchanges.

---

## JavaScript (Pact JS)

### Asynchronous Consumer Test

```javascript
import { Pact } from "@pact-foundation/pact";
import { v4SynchronousBodyHandler } from "@pact-foundation/pact";

const messagePact = new Pact({
  consumer: "MyJSMessageConsumer",
  provider: "MyJSMessageProvider",
});

describe("async message consumer", () => {
  it("can handle a dog event", async () => {
    await messagePact
      .addAsynchronousInteraction()
      .given("some state")
      .expectsToReceive("a dog event")
      .withContent({
        id: like(1),
        name: like("Fido"),
        type: like("dog"),
      })
      .withMetadata({ contentType: "application/json" })
      .executeTest(v4SynchronousBodyHandler(dogApiHandler));
  });
});
```

Key points:

- Use `addAsynchronousInteraction()` (not `addInteraction()` for HTTP)
- The actual message body is in the `Message.content` attribute
- Use `v4SynchronousBodyHandler` to wrap your handler if it's synchronous
- All handlers must return `Promise<any>`

### Asynchronous Provider (Producer) Test

```javascript
import {
  MessageProviderPact,
  providerWithMetadata,
} from "@pact-foundation/pact";

const p = new MessageProviderPact({
  messageProviders: {
    "a dog event": providerWithMetadata(
      () => createDog(27), // returns the message payload
      { queue: "animals" }, // optional metadata
    ),
  },
  provider: "MyJSMessageProvider",
  providerVersion: "1.0.0",
  pactUrls: ["./pacts/myjsmessageconsumer-myjsmessageprovider.json"],
});

p.verify();
```

### Synchronous Consumer Test (gRPC / WebSocket)

```javascript
await pact
  .addSynchronousInteraction("a file upload request")
  .given("file upload service is available")
  .withRequest((builder) => {
    builder.withJSONContent({
      filename: like("document.pdf"),
      size: integer(1024),
    });
  })
  .withResponse((builder) => {
    builder.withJSONContent({
      id: like("upload-1"),
      message: like("Upload successful"),
    });
  })
  .executeTest(async (message) => {
    const response = message.Response[0];
    const responseData = JSON.parse(response.content.toString());
    expect(responseData.message).to.equal("Upload successful");
  });
```

---

## Java / Kafka (JVM)

### Kafka JSON Consumer Test

```java
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(
  providerName = "jsonKafkaProviderApp",
  providerType = ProviderType.ASYNCH,
  pactVersion = PactSpecVersion.V3
)
class JsonKafkaConsumerTest {

  @Pact(consumer = "jsonKafkaConsumerApp")
  MessagePact simpleJsonPact(MessagePactBuilder builder) {
    PactDslJsonBody body = new PactDslJsonBody();
    body.stringType("name", "almost-anything");

    return builder
      .expectsToReceive("A simple message")
      .withMetadata(Map.of("contentType", "application/json"))
      .withContent(body)
      .toPact();
  }

  @Test
  @PactTestFor(pactMethod = "simpleJsonPact", providerType = ProviderType.ASYNCH)
  void simpleMessage(List<Message> messages) {
    byte[] kafkaBytes = messages.get(0).contentsAsBytes();
    // Use production deserialization code:
    ConsumerDomainRecord record = productionDeserializer.deserialize("", kafkaBytes);
    assertDoesNotThrow(() -> productionCode.handle(record));
  }
}
```

### Kafka JSON Provider Test

```java
@Provider("jsonKafkaProviderApp")
@Consumer("jsonKafkaConsumerApp")
@PactBroker(url = "http://localhost:9292")
class JsonKafkaProviderTest {

  @BeforeEach
  void before(PactVerificationContext context) {
    context.setTarget(new MessageTestTarget());
  }

  @TestTemplate
  @ExtendWith(PactVerificationInvocationContextProvider.class)
  void pactVerificationTestTemplate(PactVerificationContext context) {
    context.verifyInteraction();
  }

  @PactVerifyProvider("A simple message")
  MessageAndMetadata verifySimpleMessageEvent() {
    Map<String, Object> metadata = Map.of("contentType", "application/json");
    ProviderDomainRecord record = new ProviderDomainRecord("name");
    KafkaJsonSerializer<ProviderDomainRecord> serializer = createProductionSerializer();
    byte[] bytes = serializer.serialize("", record);
    return new MessageAndMetadata(bytes, metadata);
  }
}
```

### Schema Registry (Confluent) — Magic Bytes

When using Confluent Schema Registry, messages are prefixed with 5 "magic bytes". Set the contentType to `application/vnd.schemaregistry.v1+json` in the pact metadata — Pact will automatically prepend/strip these bytes:

```java
return builder
  .expectsToReceive("A json schema message")
  .withMetadata(Map.of("contentType", "application/vnd.schemaregistry.v1+json"))
  .withContent(body)
  .toPact();
```

Use a `MockSchemaRegistryClient` in tests (so it doesn't hit a real registry):

```java
private MockSchemaRegistryClient schemaRegistryClient = new MockSchemaRegistryClient();
```

---

## Optional Fields in Message Responses

Optional fields in message payloads follow the same patterns as HTTP optional fields:

1. **Provider States** — write separate interactions for "field present" vs "field absent"
2. **Array Contains** (Pact V4) — for arrays containing objects with different shapes

See `references/pact-recipes.md` for full examples.

---

## Message Pact File Structure

A generated message pact file looks like:

```json
{
  "consumer": { "name": "jsonKafkaConsumerApp" },
  "messages": [
    {
      "_id": "753ff77671f87af7045426b6f333e767315fbf2e",
      "contents": { "name": "almost-anything" },
      "description": "A simple message",
      "matchingRules": {
        "body": {
          "$.name": { "combine": "AND", "matchers": [{ "match": "type" }] }
        }
      },
      "metaData": { "contentType": "application/json" }
    }
  ],
  "metadata": {
    "pactSpecification": { "version": "3.0.0" }
  },
  "provider": { "name": "jsonKafkaProviderApp" }
}
```

Key differences from HTTP pact files:

- Top-level key is `messages` (not `interactions`)
- Each message has `contents` (not `request`/`response`)
- Matching rules apply to `body` (the message contents)
- `metaData` carries content-type and other transport hints

---

## Publishing Message Pacts

Message pact files are published to the Pact Broker exactly like HTTP pact files:

```
contract-testing_publish_consumer_contracts
  pacticipantName: "MyKafkaConsumer"
  pacticipantVersionNumber: "<git-sha>"
  branch: "<git-branch>"
  contracts: [
    {
      consumers: ["MyKafkaProvider"],
      pact: "<base64-encoded pact file contents>"
    }
  ]
```

Provider verification results are also published back automatically by the Pact library.


path: references/workflow.md
content: # End-to-End Contract Testing Workflow

This guide covers the two main workflows — Standard Pact and Bi-Directional Contract Testing (BDCT) — with exact tool calls at each step. For BDCT specifics, see `bdct.md`.

---

## Standard Pact Workflow (Consumer-Driven)

### Step 1: Consumer writes tests and generates a pact file

The consumer's test suite (using pact-js, pact-jvm, pact-go, etc.) generates a `.json` pact file. No MCP tool needed — this happens in the consumer's CI pipeline.

If the consumer doesn't have tests yet, generate them with AI:

```
contract-testing_generate_pact_tests
  language: "javascript"           # or java, go, python, ruby, etc.
  request_response:
    request: "GET /users/123 HTTP/1.1"
    response: "200 OK { id: 123, name: 'Alice' }"
  additional_instructions: "use pact-js v13, vitest"
```

Before generating, fetch existing provider states so the AI reuses them:

```
contract-testing_get_provider_states
  provider: "UserService"
```

### Step 2: Publish the pact

Run after consumer tests pass in CI:

```
contract-testing_publish_consumer_contracts
  pacticipantName: "FrontendApp"
  pacticipantVersionNumber: "abc1234"   # git SHA or semver
  branch: "main"
  contracts:
    - consumerName: "FrontendApp"
      providerName: "UserService"
      content: "<base64-encoded pact JSON>"
      contentType: "application/json"
      specification: "pact"
  buildUrl: "https://ci.example.com/builds/42"
```

### Step 3: Provider fetches pacts to verify

In the provider's CI pipeline:

```
contract-testing_get_pacts_for_verification
  providerName: "UserService"
  providerVersionBranch: "main"
  consumerVersionSelectors:
    - { mainBranch: true }
    - { deployedOrReleased: true }
  includePendingStatus: true
  includeWipPactsSince: "2024-01-01"
```

- `mainBranch: true` — verify the latest consumer main branch pact
- `deployedOrReleased: true` — verify pacts from versions already in any environment
- `includePendingStatus` — allows new consumer pacts to fail without breaking the provider build (pending pacts)
- `includeWipPactsSince` — include WIP pacts (new consumer interactions not yet verified) for early feedback

### Step 4: Provider verifies and publishes results

The provider runs its verification suite. Results are published back to the broker automatically by the Pact library (no MCP tool needed — handled by the pact-verifier).

### Step 5: Check can-i-deploy

Run this in CI as a gate **before** deploying:

```
contract-testing_can_i_deploy
  pacticipant: "FrontendApp"
  version: "abc1234"
  environment: "production"
```

- Returns success (exit 0) or failure (exit 1) with a human-readable explanation
- If it fails, use the Matrix to diagnose (see below)

### Step 6: Deploy and record

After a successful deploy:

```
contract-testing_record_deployment
  pacticipantName: "FrontendApp"
  versionNumber: "abc1234"
  environmentId: "<uuid-of-production>"
```

Get the environment UUID if you don't have it:

```
contract-testing_list_environments
```

For blue/green deployments, use `applicationInstance: "blue"` or `"green"` in record-deployment.

---

## Diagnosing a Failed can-i-deploy

When can-i-deploy fails, use the Matrix to understand why:

```
contract-testing_matrix
  q:
    - { pacticipant: "FrontendApp", latest: true, branch: "main" }
    - { pacticipant: "UserService", deployed: true, environment: "production" }
  latestby: "cvp"
```

The matrix shows every consumer-provider version combination and its verification status. Look for rows marked as failed and check the reason — usually an unverified or failing interaction.

---

## Setting Up a New Service

```
# 1. Register the service
contract-testing_create_pacticipant
  name: "PaymentService"
  displayName: "Payment Service"
  mainBranch: "main"
  repositoryUrl: "https://github.com/example/payment-service"

# 2. Confirm it was created
contract-testing_get_pacticipant
  pacticipantName: "PaymentService"

# 3. Discover existing environments (get their UUIDs)
contract-testing_list_environments

# 4. Create new environments if needed
contract-testing_create_environment
  name: "staging"
  displayName: "Staging"
  production: false
```

---

## Mobile / Library Release Workflow

For services where multiple versions coexist simultaneously (mobile apps, shared libraries):

```
# After release (not deploy)
contract-testing_record_release
  pacticipantName: "MobileApp"
  versionNumber: "3.2.1"
  environmentId: "<uuid-of-production>"

# Check what's still supported
contract-testing_get_currently_supported_versions
  environmentId: "<uuid-of-production>"
```

Unlike `record_deployment` (which supersedes the previous version), `record_release` keeps all released versions marked as "supported" simultaneously.

---

## Keeping the Workspace Clean

```
# Remove stale feature branches after merge
contract-testing_delete_branch
  pacticipantName: "FrontendApp"
  branchName: "feature/old-feature"

# Remove a decommissioned service
contract-testing_delete_pacticipant
  pacticipantName: "LegacyService"
```


path: references/dsl.java.md
content: While you already know this, here is a reminder of the Pact-JVM classes and methods you will need to use to create a Pact test in Java (having omitted deprecated and unadvised methods):

```java
class LambdaDslJsonBody:
    fn build() -> DslPart
class LambdaDsl:
    fn newJsonArray(array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinLike(size: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMaxLike(size: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinMaxLike(minSize: Integer, maxSize: Integer, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayUnordered(array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinUnordered(size: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMaxUnordered(size: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonArrayMinMaxUnordered(minSize: int, maxSize: int, array: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn newJsonBody(array: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonBody
public interface BodyBuilder:
    fn getMatchers() -> MatchingRuleCategory
    fn getGenerators() -> Generators
    fn getContentType() -> ContentType
    fn buildBody() -> byte[]
    fn getHeaderMatchers() -> MatchingRuleCategory
class LambdaDslObject:
    fn getPactDslObject() -> PactDslJsonBody
    fn stringValue(name: String, value: String) -> LambdaDslObject
    fn stringType(name: String, example: String) -> LambdaDslObject
    fn stringType(name: String) -> LambdaDslObject
    fn stringType(names: String...) -> LambdaDslObject
    fn stringMatcher(name: String, regex: String) -> LambdaDslObject
    fn stringMatcher(name: String, regex: String, example: String) -> LambdaDslObject
    fn numberValue(name: String, value: Number) -> LambdaDslObject
    fn numberType(name: String, example: Number) -> LambdaDslObject
    fn numberType(names: String...) -> LambdaDslObject
    fn integerType(name: String, example: Integer) -> LambdaDslObject
    fn integerType(names: String...) -> LambdaDslObject
    fn decimalType(name: String, example: BigDecimal) -> LambdaDslObject
    fn decimalType(name: String, example: Double) -> LambdaDslObject
    fn decimalType(names: String...) -> LambdaDslObject
    fn numberMatching(name: String, regex: String, example: Number) -> LambdaDslObject
    fn decimalMatching(name: String, regex: String, example: Double) -> LambdaDslObject
    fn integerMatching(name: String, regex: String, example: Integer) -> LambdaDslObject
    fn booleanValue(name: String, value: Boolean) -> LambdaDslObject
    fn booleanType(name: String, example: Boolean) -> LambdaDslObject
    fn booleanType(names: String...) -> LambdaDslObject
    fn id() -> LambdaDslObject
    fn id(name: String) -> LambdaDslObject
    fn id(name: String, example: Long) -> LambdaDslObject
    fn uuid(name: String) -> LambdaDslObject
    fn uuid(name: String, example: UUID) -> LambdaDslObject
    fn date() -> LambdaDslObject
    fn date(name: String) -> LambdaDslObject
    fn date(name: String, format: String) -> LambdaDslObject
    fn date(name: String, format: String, example: Date) -> LambdaDslObject
    fn date(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn date(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn date(name: String, format: String, example: LocalDate) -> LambdaDslObject
    fn time() -> LambdaDslObject
    fn time(name: String) -> LambdaDslObject
    fn time(name: String, format: String) -> LambdaDslObject
    fn time(name: String, format: String, example: Date) -> LambdaDslObject
    fn time(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn time(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn datetime(name: String, format: String) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Date) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Instant) -> LambdaDslObject
    fn datetime(name: String, format: String, example: Date, timeZone: TimeZone) -> LambdaDslObject
    fn datetime(name: String, format: String, example: ZonedDateTime) -> LambdaDslObject
    fn ipV4Address(name: String) -> LambdaDslObject
    fn valueFromProviderState(name: String, expression: String, example: Object) -> LambdaDslObject
    fn and(name: String, value: Object, rules: MatchingRule...) -> LambdaDslObject
    fn or(name: String, value: Object, rules: MatchingRule...) -> LambdaDslObject
    fn array(name: String, array: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn object(name: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachLike(name: String, value: PactDslJsonRootValue) -> LambdaDslObject
    fn eachLike(name: String, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minArrayLike(name: String, size: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn maxArrayLike(name: String, size: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, numberExamples: Int, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn minMaxArrayLike(name: String, minSize: Integer, maxSize: Integer, value: PactDslJsonRootValue, numberExamples: Int) -> LambdaDslObject
    fn nullValue(fieldName: String) -> LambdaDslObject
    fn eachArrayLike(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayLike(name: String, numberExamples: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMaxLike(name: String, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMaxLike(name: String, numberExamples: Int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinLike(name: String, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinLike(name: String, numberExamples: Int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinMaxLike(name: String, minSize: Integer, maxSize: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachArrayWithMinMaxLike(name: String, minSize: Integer, maxSize: Integer, numberExamples: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn eachKeyMappedToAnArrayLike(exampleKey: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachKeyLike(exampleKey: String, nestedObject: Consumer<LambdaDslObject>) -> LambdaDslObject
    fn eachKeyLike(exampleKey: String, value: PactDslJsonRootValue) -> LambdaDslObject
    fn dateExpression(name: String, expression: String) -> LambdaDslObject
    fn dateExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn timeExpression(name: String, expression: String) -> LambdaDslObject
    fn timeExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn datetimeExpression(name: String, expression: String) -> LambdaDslObject
    fn datetimeExpression(name: String, expression: String, format: String) -> LambdaDslObject
    fn unorderedArray(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMinArray(name: String, size: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMaxArray(name: String, size: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn unorderedMinMaxArray(name: String, minSize: Int, maxSize: Int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
    fn matchUrl(name: String, basePath: String, pathFragments: Object...) -> LambdaDslObject
    fn matchUrl2(name: String, pathFragments: Object...) -> LambdaDslObject
    fn arrayContaining(name: String, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslObject
class LambdaDslJsonArray:
    fn getPactDslJsonArray() -> PactDslJsonArray
    fn object(o: Consumer<LambdaDslObject>) -> LambdaDslJsonArray
    fn array(a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedArray(a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMinArray(size: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMaxArray(size: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn unorderedMinMaxArray(minSize: int, maxSize: int, a: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn stringValue(value: String) -> LambdaDslJsonArray
    fn stringType(example: String) -> LambdaDslJsonArray
    fn stringMatcher(regex: String, example: String) -> LambdaDslJsonArray
    fn numberValue(value: Number) -> LambdaDslJsonArray
    fn numberType(example: Number) -> LambdaDslJsonArray
    fn integerType() -> LambdaDslJsonArray
    fn integerType(example: Long) -> LambdaDslJsonArray
    fn decimalType() -> LambdaDslJsonArray
    fn decimalType(example: BigDecimal) -> LambdaDslJsonArray
    fn decimalType(example: Double) -> LambdaDslJsonArray
    fn numberMatching(regex: String, example: Number) -> LambdaDslJsonArray
    fn decimalMatching(regex: String, example: Double) -> LambdaDslJsonArray
    fn integerMatching(regex: String, example: Integer) -> LambdaDslJsonArray
    fn booleanValue(value: Boolean) -> LambdaDslJsonArray
    fn booleanType(example: Boolean) -> LambdaDslJsonArray
    fn date() -> LambdaDslJsonArray
    fn date(format: String) -> LambdaDslJsonArray
    fn date(format: String, example: Date) -> LambdaDslJsonArray
    fn time() -> LambdaDslJsonArray
    fn time(format: String) -> LambdaDslJsonArray
    fn time(format: String, example: Date) -> LambdaDslJsonArray
    fn id() -> LambdaDslJsonArray
    fn id(example: Long) -> LambdaDslJsonArray
    fn uuid() -> LambdaDslJsonArray
    fn uuid(example: String) -> LambdaDslJsonArray
    fn hexValue() -> LambdaDslJsonArray
    fn hexValue(example: String) -> LambdaDslJsonArray
    fn ipV4Address() -> LambdaDslJsonArray
    fn and(value: Object, rules: MatchingRule...) -> LambdaDslJsonArray
    fn or(value: Object, rules: MatchingRule...) -> LambdaDslJsonArray
    fn eachLike(nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn eachLike(value: PactDslJsonRootValue) -> LambdaDslJsonArray
    fn eachLike(value: PactDslJsonRootValue, numberExamples: int) -> LambdaDslJsonArray
    fn eachLike(numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minArrayLike(size: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minArrayLike(size: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn maxArrayLike(size: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn maxArrayLike(size: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minMaxArrayLike(minSize: Integer, maxSize: Integer, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn minMaxArrayLike(minSize: Integer, maxSize: Integer, numberExamples: int, nestedObject: Consumer<LambdaDslJsonBody>) -> LambdaDslJsonArray
    fn nullValue() -> LambdaDslJsonArray
    fn eachArrayLike(nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayLike(numberExamples: int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMaxLike(size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMaxLike(numberExamples: int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinLike(size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinLike(numberExamples: int, size: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinMaxLike(minSize: Integer, maxSize: Integer, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn eachArrayWithMinMaxLike(minSize: Integer, maxSize: Integer, numberExamples: int, nestedArray: Consumer<LambdaDslJsonArray>) -> LambdaDslJsonArray
    fn dateExpression(expression: String) -> LambdaDslJsonArray
    fn dateExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn timeExpression(expression: String) -> LambdaDslJsonArray
    fn timeExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn datetimeExpression(expression: String) -> LambdaDslJsonArray
    fn datetimeExpression(expression: String, format: String) -> LambdaDslJsonArray
    fn build() -> DslPart
class QuoteUtil:
    fn convert(text: String) -> String
abstract class DslPart:
    fn putObjectPrivate(obj: DslPart) -> Unit
    fn putArrayPrivate(obj: DslPart) -> Unit
    fn array(name: String) -> PactDslJsonArray
    fn array() -> PactDslJsonArray
    fn unorderedArray(name: String) -> PactDslJsonArray
    fn unorderedArray() -> PactDslJsonArray
    fn unorderedMinArray(name: String, size: Int) -> PactDslJsonArray
    fn unorderedMinArray(size: Int) -> PactDslJsonArray
    fn unorderedMaxArray(name: String, size: Int) -> PactDslJsonArray
    fn unorderedMaxArray(size: Int) -> PactDslJsonArray
    fn unorderedMinMaxArray(name: String, minSize: Int, maxSize: Int) -> PactDslJsonArray
    fn unorderedMinMaxArray(minSize: Int, maxSize: Int) -> PactDslJsonArray
    fn closeArray() -> DslPart?
    fn eachLike(name: String) -> PactDslJsonBody
    fn eachLike(name: String, obj: DslPart) -> PactDslJsonBody
    fn eachLike() -> PactDslJsonBody
    fn eachLike(obj: DslPart) -> PactDslJsonArray
    fn eachLike(name: String, numberExamples: Int) -> PactDslJsonBody
    fn eachLike(numberExamples: Int) -> PactDslJsonBody
    fn minArrayLike(name: String, size: Int) -> PactDslJsonBody
    fn minArrayLike(size: Int) -> PactDslJsonBody
    fn minArrayLike(name: String, size: Int, obj: DslPart) -> PactDslJsonBody
    fn minArrayLike(size: Int, obj: DslPart) -> PactDslJsonArray
    fn minArrayLike(name: String, size: Int, numberExamples: Int) -> PactDslJsonBody
    fn minArrayLike(size: Int, numberExamples: Int) -> PactDslJsonBody
    fn maxArrayLike(name: String, size: Int) -> PactDslJsonBody
    fn maxArrayLike(size: Int) -> PactDsl
```

You want to encourage your organisation to use the more modern `LambdaDsl` classes and methods when writing Pact tests. So instead of:

```java
PactDslJsonBody requestBody = new PactDslJsonBody()
    .stringValue("title", "Pact is awesome!")
    .numberType("userId", 42);
```

use:

```java
PactDslJsonBody requestBody = newJsonBody((o) -> {
            o.stringType("title", "Pact is awesome!");
            o.numberType("userId", 42);
        }).build();
```


path: references/dsl.dotnet.md
content: While you already know this, here is a reminder of the Pact .NET interfaces, types, classes and methods you will need to use to create a Pact test in C# (having omitted deprecated and unadvised methods):

**File: src/PactNet/IRequestBuilderV2.cs**
```
namespace PactNet
{
    public interface IRequestBuilderV2
    {
        IRequestBuilderV2 Given(string providerState);
        IRequestBuilderV2 WithRequest(HttpMethod method, string path);
        IRequestBuilderV2 WithRequest(string method, string path);
        IRequestBuilderV2 WithQuery(string key, string value);
        IRequestBuilderV2 WithHeader(string key, string value);
        IRequestBuilderV2 WithHeader(string key, IMatcher matcher);
        IRequestBuilderV2 WithJsonBody(dynamic body);
        IRequestBuilderV2 WithJsonBody(dynamic body, string contentType);
        IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IRequestBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IRequestBuilderV2 WithBody(string body, string contentType);
        IResponseBuilderV2 WillRespond();
    }
}
```

**File: src/PactNet/IRequestBuilderV3.cs**
```
namespace PactNet
{
    public interface IRequestBuilderV3
    {
        IRequestBuilderV3 Given(string providerState);
        IRequestBuilderV3 Given(string providerState, IDictionary<string, string> parameters);
        IRequestBuilderV3 WithRequest(HttpMethod method, string path);
        IRequestBuilderV3 WithRequest(string method, string path);
        IRequestBuilderV3 WithQuery(string key, string value);
        IRequestBuilderV3 WithQuery(string key, IMatcher matcher);
        IRequestBuilderV3 WithHeader(string key, string value);
        IRequestBuilderV3 WithHeader(string key, IMatcher matcher);
        IRequestBuilderV3 WithJsonBody(dynamic body);
        IRequestBuilderV3 WithJsonBody(dynamic body, string contentType);
        IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IRequestBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IRequestBuilderV3 WithBody(string body, string contentType);
        IResponseBuilderV3 WillRespond();
    }
}
```

**File: src/PactNet/IRequestBuilderV4.cs**
```
namespace PactNet
{
    public interface IRequestBuilderV4
    {
        IRequestBuilderV4 Given(string providerState);
        IRequestBuilderV4 Given(string providerState, IDictionary<string, string> parameters);
        IRequestBuilderV4 WithRequest(HttpMethod method, string path);
        IRequestBuilderV4 WithRequest(string method, string path);
        IRequestBuilderV4 WithQuery(string key, string value);
        IRequestBuilderV4 WithQuery(string key, IMatcher matcher);
        IRequestBuilderV4 WithHeader(string key, string value);
        IRequestBuilderV4 WithHeader(string key, IMatcher matcher);
        IRequestBuilderV4 WithJsonBody(dynamic body);
        IRequestBuilderV4 WithJsonBody(dynamic body, string contentType);
        IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IRequestBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IRequestBuilderV4 WithBody(string body, string contentType);
        IResponseBuilderV4 WillRespond();
    }
}
```

**File: src/PactNet/IResponseBuilderV2.cs**
```
namespace PactNet
{
    public interface IResponseBuilderV2
    {
        IResponseBuilderV2 WithStatus(HttpStatusCode status);
        IResponseBuilderV2 WithHeader(string key, string value);
        IResponseBuilderV2 WithHeader(string key, IMatcher matcher);
        IResponseBuilderV2 WithJsonBody(dynamic body);
        IResponseBuilderV2 WithJsonBody(dynamic body, string contentType);
        IResponseBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IResponseBuilderV2 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IResponseBuilderV2 WithBody(string body, string contentType);
    }
}
```

**File: src/PactNet/IResponseBuilderV3.cs**
```
namespace PactNet
{
    public interface IResponseBuilderV3
    {
        IResponseBuilderV3 WithStatus(HttpStatusCode status);
        IResponseBuilderV3 WithHeader(string key, string value);
        IResponseBuilderV3 WithHeader(string key, IMatcher matcher);
        IResponseBuilderV3 WithJsonBody(dynamic body);
        IResponseBuilderV3 WithJsonBody(dynamic body, string contentType);
        IResponseBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IResponseBuilderV3 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IResponseBuilderV3 WithBody(string body, string contentType);
    }
}
```

**File: src/PactNet/IResponseBuilderV4.cs**
```
namespace PactNet
{
    public interface IResponseBuilderV4
    {
        IResponseBuilderV4 WithStatus(HttpStatusCode status);
        IResponseBuilderV4 WithHeader(string key, string value);
        IResponseBuilderV4 WithHeader(string key, IMatcher matcher);
        IResponseBuilderV4 WithJsonBody(dynamic body);
        IResponseBuilderV4 WithJsonBody(dynamic body, string contentType);
        IResponseBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings);
        IResponseBuilderV4 WithJsonBody(dynamic body, JsonSerializerOptions settings, string contentType);
        IResponseBuilderV4 WithBody(string body, string contentType);
    }
}
```

**File: src/PactNet/IConsumerContext.cs**
```
namespace PactNet
{
    public interface IConsumerContext
    {
        Uri MockServerUri { get; }
    }
}
```

**File: src/PactNet/IPactBuilderV2.cs**
```
namespace PactNet
{
    public interface IPactBuilderV2
    {
        IRequestBuilderV2 UponReceiving(string description);
        void Verify(Action<IConsumerContext> interact);
        Task VerifyAsync(Func<IConsumerContext, Task> interact);
    }
}
```

**File: src/PactNet/IPactBuilderV3.cs**
```
namespace PactNet
{
    public interface IPactBuilderV3
    {
        IRequestBuilderV3 UponReceiving(string description);
        void Verify(Action<IConsumerContext> interact);
        Task VerifyAsync(Func<IConsumerContext, Task> interact);
    }
}
```

**File: src/PactNet/IPactBuilderV4.cs**
```
namespace PactNet
{
    public interface IPactBuilderV4
    {
        IRequestBuilderV4 UponReceiving(string description);
        void Verify(Action<IConsumerContext> interact);
        Task VerifyAsync(Func<IConsumerContext, Task> interact);
    }
}
```

**File: src/PactNet/Pact.cs**
```
namespace PactNet
{
    public static class Pact
    {
        public static IPactBuilderV2 V2(string consumer, string provider, PactConfig config);
        public static IPactBuilderV3 V3(string consumer, string provider, PactConfig config);
        public static IPactBuilderV4 V4(string consumer, string provider, PactConfig config);
    }
}
```

**File: src/PactNet/PactConfig.cs**
```
namespace PactNet
{
    public class PactConfig
    {
        public string PactDir { get; set; }
        public string LogDir { get; set; }
        public JsonSerializerOptions DefaultJsonSettings { get; set; }
        public Action<string> Outputters { get; set; }
    }
}
```

File: src/PactNet.Abstractions/Matchers/Match.cs
```
namespace PactNet.Matchers
{
    public static class Match
    {
        public static IMatcher Regex(string example, string pattern);
        public static IMatcher Type(object example);
        public static IMatcher MinType(object example, int min);
        public static IMatcher MaxType(object example, int max);
        public static IMatcher Decimal(decimal example);
        public static IMatcher Number(int example);
        public static IMatcher Number(double example);
        public static IMatcher Number(float example);
        public static IMatcher Number(decimal example);
        public static IMatcher Equality(dynamic example);
        public static IMatcher Null();
        public static IMatcher Include(string example);
    }
}
```

File: src/PactNet.Abstractions/PactConfig.cs
```
namespace PactNet
{
    public class PactConfig
    {
        public string PactDir { get; set; }
        public string LogDir { get; set; }
        public JsonSerializerOptions DefaultJsonSettings { get; set; }
        public Action<string> Outputters { get; set; }
    }
}
```

File: src/PactNet.Abstractions/Infrastructure/Outputters/IOutput.cs
```
namespace PactNet.Infrastructure.Outputters
{
    public interface IOutput
    {
        void WriteLine(string line);
    }
}
```

Make sure to use `Pact.V4` interface from the latest version (5.x.x) of the Pact .NET library, as this is the most up-to-date version.


path: references/dsl.javascript.md
content: While you already know this, here is a reminder of the Pact-JS classes and methods you will need to use to create a Pact test in TypeScript (having omitted deprecated and unadvised methods):

File: /v3/pact.ts
```
// import { PactV3 } from "@pact-foundation/pact";
class PactV3 {
    constructor(opts: PactV3Options);
    addInteraction(interaction: V3Interaction): PactV3;
    given(providerState: string, parameters?: JsonMap): PactV3;
    uponReceiving(description: string): PactV3;
    withRequest(req: V3Request): PactV3;
    withRequestBinaryFile(req: V3Request, contentType: string, file: string): PactV3;
    withRequestMultipartFileUpload(req: V3Request, contentType: string, file: string, mimePartName: string): PactV3;
    willRespondWith(res: V3Response): PactV3;
    withResponseBinaryFile(res: V3Response, contentType: string, file: string): PactV3;
    withResponseMultipartFileUpload(res: V3Response, contentType: string, file: string, mimePartName: string): PactV3;
    executeTest<T>(testFn: (mockServer: V3MockServer) => Promise<T>): Promise<T | undefined>;
}
```

File: /v3/matchers.ts
```
// import { MatchersV3 } from "@pact-foundation/pact";
module MatchersV3 {
    function isMatcher(x: unknown): x is Matcher<unknown>;
    const like: <T>(template: T) => Matcher<T>;
    const eachKeyLike: <T>(keyTemplate: string, template: T) => Matcher<T>;
    const eachKeyMatches: (example: Record<string, unknown>, matchers?: Matcher<string> | Matcher<string>[]) => RulesMatcher<unknown>;
    const eachValueMatches: <T>(example: Record<string, T>, matchers: Matcher<T> | Matcher<T>[]) => RulesMatcher<T>;
    const eachLike: <T>(template: T, min?: number) => MinLikeMatcher<T[]>;
    const atLeastOneLike: <T>(template: T, count?: number) => MinLikeMatcher<T[]>;
    const atLeastLike: <T>(template: T, min: number, count?: number) => MinLikeMatcher<T[]>;
    const atMostLike: <T>(template: T, max: number, count?: number) => MaxLikeMatcher<T[]>;
    const constrainedArrayLike: <T>(template: T, min: number, max: number, count?: number) => MinLikeMatcher<T[]> & MaxLikeMatcher<T[]>;
    const boolean: (b?: boolean) => Matcher<boolean>;
    const integer: (int?: number) => Matcher<number>;
    function number(num?: number): Matcher<number>;
    function string(str?: string): Matcher<string>;
    function regex(pattern: RegExp | string, str: string): V3RegexMatcher;
    const equal: <T>(value: T) => Matcher<T>;
    function datetime(format: string, example: string): DateTimeMatcher;
    function timestamp(format: string, example: string): DateTimeMatcher;
    function time(format: string, example: string): DateTimeMatcher;
    function date(format: string, example: string): DateTimeMatcher;
    function includes(value: string): Matcher<string>;
    function nullValue(): Matcher<null>;
    function url2(basePath: string | null, pathFragments: Array<string | V3RegexMatcher | RegExp>): V3RegexMatcher;
    function url(pathFragments: Array<string | V3RegexMatcher | RegExp>): V3RegexMatcher;
    function arrayContaining(...variants: unknown[]): ArrayContainsMatcher;
    function fromProviderState<V>(expression: string, exampleValue: V): ProviderStateInjectedValue<V>;
    function uuid(example?: string): V3RegexMatcher;
    const matcherValueOrString: (obj: unknown) => string;
    function reify(input: unknown): AnyJson;
    const extractPayload: typeof reify;
}
```

File: /v3/xml/xmlBuilder.ts
```
class XmlBuilder {
    build(callback: (doc: XmlElement) => void): string;
}
```

File: /v3/xml/xmlElement.ts
```
type XmlAttributes = Map<string, string>;
type XmlCallback = (n: XmlElement) => void;

class XmlElement {
    setName(name: string): XmlElement;
    setAttributes(attributes: XmlAttributes): XmlElement;
    appendElement(name: string, attributes: XmlAttributes, arg?: string | XmlCallback | Matcher<string>): XmlElement;
    appendText(content: string | Matcher<string>): XmlElement;
    eachLike(name: string, attributes: XmlAttributes, cb?: XmlCallback, options: EachLikeOptions = { examples: 1 }): XmlElement;
}

interface EachLikeOptions {
    min?: number;
    max?: number;
    examples?: number;
}
```

File: /v3/xml/xmlNode.ts
```
class XmlNode {}
```

File: /v3/xml/xmlText.ts
```
class XmlText {
    constructor(content: string, matcher?: Matcher<string>);
}
```

File: /dsl/interaction.ts
```
interface QueryObject {
  [name: string]: string | Matcher<string> | string[];
}
type Query = string | QueryObject;
type Headers = {
  [header: string]: string | Matcher<string> | (Matcher<string> | string)[];
};
interface RequestOptions {
  method: HTTPMethods | HTTPMethod;
  path: string | Matcher<string>;
  query?: Query;
  headers?: Headers;
  body?: AnyTemplate;
}
interface ResponseOptions {
  status: number;
  headers?: Headers;
  body?: AnyTemplate;
}
interface InteractionObject {
  state: string | undefined;
  uponReceiving: string;
  withRequest: RequestOptions;
  willRespondWith: ResponseOptions;
}
interface InteractionState {
  providerState?: string;
  description?: string;
  request?: RequestOptions;
  response?: ResponseOptions;
}
interface InteractionStateComplete {
  providerState?: string;
  description: string;
  request: RequestOptions;
  response: ResponseOptions;
}
class Interaction {
  given(providerState: string): this;
  uponReceiving(description: string): this;
  withRequest(requestOpts: RequestOptions): this;
  willRespondWith(responseOpts: ResponseOptions): this;
  json(): InteractionStateComplete;
}
const interactionToInteractionObject(interaction: InteractionStateComplete): InteractionObject;
```

File: /dsl/options.ts
```
type LogLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error';
interface PactOptions {
    consumer: string;
    provider: string;
    port?: number;
    host?: string;
    ssl?: boolean;
    sslcert?: string;
    sslkey?: string;
    dir?: string;
    log?: string;
    logLevel?: LogLevel;
    spec?: number;
    cors?: boolean;
    timeout?: number;
    pactfileWriteMode?: PactfileWriteMode;
}
interface MandatoryPactOptions {
    port: number;
    host: string;
    ssl: boolean;
}
type PactOptionsComplete = PactOptions & MandatoryPactOptions;
interface MessageProviderOptions {
    logLevel?: LogLevel;
    messageProviders: MessageProviders;
    stateHandlers?: MessageStateHandlers;
}
type ExcludedPactNodeVerifierKeys = Exclude<keyof PactCoreVerifierOptions, 'providerBaseUrl'>;
type PactNodeVerificationExcludedOptions = Pick<PactCoreVerifierOptions, ExcludedPactNodeVerifierKeys>;
type PactMessageProviderOptions = PactNodeVerificationExcludedOptions & MessageProviderOptions;
interface MessageConsumerOptions {
    consumer: string;
    dir?: string;
    provider: string;
    log?: string;
    logLevel?: LogLevel;
    spec?: number;
    pactfileWriteMode?: PactfileWriteMode;
}
```

File: /dsl/verifier/verifier.ts
```
class Verifier {
    constructor(config: VerifierOptions);
    verifyProvider(): Promise<string>;
}
```

File: /dsl/verifier/proxy/messages.ts
```
export const providerWithMetadata(provider: MessageProvider, metadata: Record<string, string>): MessageProvider;
```

File: /dsl/verifier/proxy/types.ts
```
type Hook: () => Promise<unknown>;
interface StateHandlers {
  [name: string]: StateHandler;
}
interface ProviderState {
  action: StateAction;
  params: JsonMap;
  state: string;
}
type StateAction: 'setup' | 'teardown';
type StateFunc: (parameters?: AnyJson) => Promise<JsonMap | void>;
type StateFuncWithSetup: {
  setup?: StateFunc;
  teardown?: StateFunc;
};
type StateHandler: StateFuncWithSetup | StateFunc;
```


path: references/pact-broker-advanced.md
content: # Pact Broker — Advanced Topics

> Sources: https://docs.pact.io/pact_broker/advanced_topics/consumer_version_selectors.md, https://docs.pact.io/pact_broker/advanced_topics/pending_pacts.md, https://docs.pact.io/pact_broker/advanced_topics/wip_pacts.md, https://docs.pact.io/pact_broker/branches.md, https://docs.pact.io/pact_broker/recording_deployments_and_releases.md, https://docs.pact.io/getting_started/versioning_in_the_pact_broker.md, https://docs.pact.io/pact_broker/webhooks.md

---

## Consumer Version Selectors

Consumer version selectors are the way to configure which pacts the provider verifies. A list of selector objects is provided, allowing flexibility in which pacts to verify.

### Default selectors

The defaults find pacts matching any of:

1. Latest version on the main branch
2. All versions currently deployed
3. All versions currently marked as released

### Selector properties

| Property                   | Description                                                                                                                              |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `mainBranch: true`         | Latest pact for the configured `mainBranch` of each consumer. Requires consumer has set `mainBranch` property.                           |
| `branch: "<name>"`         | Latest pact from a specific branch of each consumer.                                                                                     |
| `matchingBranch: true`     | Latest pact from any consumer branch with the same name as the current provider branch. Used for coordinated feature branch development. |
| `fallbackBranch: "<name>"` | Fallback if specified `branch` doesn't exist. Discouraged — use two separate selectors instead.                                          |
| `deployedOrReleased: true` | All consumer versions currently deployed OR released and supported in any environment.                                                   |
| `deployed: true`           | All consumer versions currently deployed to any environment.                                                                             |
| `released: true`           | All consumer versions currently released and supported in any environment.                                                               |
| `environment: "<name>"`    | Further qualifies `deployed` or `released` — only versions in a specific environment.                                                    |
| `tag: "<name>"`            | Legacy: consumer versions with a specific tag. Still supported; prefer `branch`.                                                         |
| `latest: true`             | Used with `tag` — returns the latest version with that tag only.                                                                         |
| `consumer: "<name>"`       | Applies a selector to a specific consumer only.                                                                                          |

### Recommended selectors

```json
[
  { "mainBranch": true },
  { "matchingBranch": true },
  { "deployedOrReleased": true }
]
```

- `mainBranch` — verifies the main development line
- `matchingBranch` — supports coordinated feature branch development without config changes
- `deployedOrReleased` — ensures no regression with already-deployed consumers

### Advanced selector examples

```json
// Only for a specific consumer
{ "branch": "feat/new-api", "consumer": "FrontendApp" }

// Branch with fallback
{ "branch": "feat/new-api", "fallbackBranch": "main" }

// Specific environment
{ "deployed": true, "environment": "production" }

// Tag-based (legacy)
{ "tag": "main", "latest": true }
{ "tag": "production", "latest": true }
```

---

## Pending Pacts

> Source: https://docs.pact.io/pact_broker/advanced_topics/pending_pacts.md

### What is it?

Enabling pending pacts ensures the pact verification task **only fails** when a breaking change causes a previously supported pact to fail. Without it, any failing pact (e.g. ones with new consumer features) will cause the provider's build to break.

**tl;dr — enable `enablePending: true` and your provider build only fails when it needs to.**

### Prerequisites

1. Consumer must configure `mainBranch` property on their pacticipant
2. Consumer must set `branch` and `version` when publishing pacts
3. Provider must set `branch` and `version` when verifying pacts
4. Set `enablePending: true` in provider verification configuration

### Why is it needed?

**Without pending pacts**: Consumer publishes a pact with a new, unsupported interaction to `main`. Next time the provider build runs, the verification fails — blocking the provider from deploying, even though the provider is still compatible with the version of the consumer that's actually deployed in production.

**With pending pacts**: The new interaction is marked as "pending" (never successfully verified from the provider's main branch). The provider build continues. A failure in a pending pact is surfaced as a warning, not a blocking failure. Once the provider successfully verifies the interaction, it becomes "not pending" and will break the build if it regresses.

### Pact becomes non-pending when

A pact is considered "pending" for a specific provider branch until the provider branch successfully verifies it. After that, any regression in that branch will break the build.

---

## Work In Progress (WIP) Pacts

> Source: https://docs.pact.io/pact_broker/advanced_topics/wip_pacts.md

### What is it?

WIP pacts is a feature that ensures any **new contracts** are automatically verified in the provider's main pipeline build without requiring configuration changes.

A "work in progress" pact is one that:

- Is the latest for its branch (or tags)
- Does NOT have a successful verification result from the current provider branch (i.e. it's in "pending" state)
- Was not explicitly specified in the consumer version selectors

### How to enable

Set `includeWipPactsSince` to a date from which you want new pacts to be included:

```javascript
// JavaScript
includeWipPactsSince: process.env.GIT_BRANCH === "main" ? "2020-01-01" : undefined,

// Ruby
include_wip_pacts_since ENV['GIT_BRANCH'] == "main" ? "2020-01-01" : nil
```

The date is required to avoid suddenly verifying hundreds of past feature pacts all at once.

Also requires `providerVersionBranch` (or `providerVersionTags`) to be set.

**Best practice**: Only enable WIP pacts on the provider's main branch. On feature branches, you typically want specific failures to block the build.

### How it works (simplified)

1. Find all pacts that are the latest for their branch ("head" pacts)
2. Discard pacts that have already been successfully verified from the provider's main branch
3. Discard pacts that are explicitly included in the consumer version selectors
4. The remaining pacts are the WIP pacts

Because WIP pacts are in "pending" state, they don't break the build (but failures are reported as warnings).

---

## Branches

> Source: https://docs.pact.io/pact_broker/branches.md

From Pact Broker v2.82.0, branches are a first-class concept. They supersede tags for tracking branches.

### Configuring the branch when publishing pacts

Always set the branch when publishing:

```bash
pact-broker publish ./pacts \
  --pacticipant YourConsumer \
  --version $GIT_COMMIT \
  --branch $GIT_BRANCH
```

Or via MCP:

```
contract-testing_publish_consumer_contracts
  pacticipantName: "YourConsumer"
  pacticipantVersionNumber: "<git-sha>"
  branch: "<git-branch>"
  contracts: [...]
```

### Configuring the main branch

Each pacticipant should have a `mainBranch` property configured. This is what the `{ mainBranch: true }` selector uses:

```
contract-testing_patch_pacticipant
  pacticipantName: "YourConsumer"
  mainBranch: "main"
```

### Branch lifecycle

- Branches are created automatically when pacts are published with a branch name
- Clean up stale feature branches after merge: `contract-testing_delete_branch`
- The latest version for a branch is the most recently published version

---

## Recording Deployments and Releases

> Source: https://docs.pact.io/pact_broker/recording_deployments_and_releases.md

The Pact Broker needs to know which versions of each application are in each environment to return correct pacts for verification and to determine safe deployments.

### record-deployment vs record-release

|                          | `record-deployment`                                                         | `record-release`                                       |
| ------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------ |
| Use for                  | APIs and consumer apps deployed to known instances                          | Mobile apps, libraries, distributed artifacts          |
| Behaviour                | Marks previous version as **undeployed** automatically                      | Does NOT change status of previously released versions |
| Multiple versions in env | No — only one version deployed at a time (or one per `applicationInstance`) | Yes — multiple versions coexist simultaneously         |
| MCP tool                 | `contract-testing_record_deployment`                                        | `contract-testing_record_release`                      |

### Recording a deployment

```
contract-testing_record_deployment
  pacticipantName: "OrderService"
  versionNumber: "abc1234"
  environmentId: "<uuid-of-production>"
  applicationInstance: "blue"  # optional, for blue/green deployments
```

### Recording a release

```
contract-testing_record_release
  pacticipantName: "MobileApp"
  versionNumber: "3.2.1"
  environmentId: "<uuid-of-production>"
```

### Getting the environment UUID

```
contract-testing_list_environments
# Returns all environments with their UUIDs
```

### Migrating from tags to environments

Tags used for environments (e.g. `pact-broker create-version-tag --tag production`) are still supported but superseded by `record-deployment`. New features may not support tags. Migrate when possible:

1. Create named environments: `contract-testing_create_environment`
2. Replace `create-version-tag --tag production` with `contract-testing_record_deployment`
3. Replace `{ tag: "production", latest: true }` selectors with `{ deployedOrReleased: true }`

---

## Versioning in the Pact Broker

> Source: https://docs.pact.io/getting_started/versioning_in_the_pact_broker.md

### Best practices for version numbers

- **Use the git SHA** as the version number — it's unambiguous and always available in CI
- Do NOT use a version number that can be re-used across builds (e.g. `1.0.0-SNAPSHOT`)
- Do NOT use random data in pacts — a new pact row is only created when pact content changes

```bash
# Good: unique, reproducible
VERSION=$(git rev-parse HEAD)

# Bad: can be overwritten
VERSION=1.0.0-SNAPSHOT
```

### Pact content versioning

The Pact Broker versions pact **content** separately from application versions. A new database row for pact content is only created when the pact content changes. Publishing the same pact content with a new version number doesn't create a new pact — it associates the existing pact with the new version.

---

## Webhooks

> Source: https://docs.pact.io/pact_broker/webhooks.md

Webhooks allow you to trigger actions (typically CI builds) when events occur in the Pact Broker.

### Key events

| Event                                                              | When triggered                                                      | Typical use                                               |
| ------------------------------------------------------------------ | ------------------------------------------------------------------- | --------------------------------------------------------- |
| `contract_published`                                               | A pact is published for the first time, or when its content changes | Trigger provider verification                             |
| `contract_requiring_verification_published`                        | A pact is published and needs verification                          | **Recommended** — trigger provider build passing pact URL |
| `verification_published`                                           | A provider publishes a verification result                          | Trigger consumer build to check can-i-deploy              |
| `provider_verification_succeeded` / `provider_verification_failed` | After verification                                                  | Trigger consumer build with success/failure               |

### Using the `contract_requiring_verification_published` event

This is the most important webhook for an efficient CI/CD workflow. When a consumer publishes a changed pact, this event triggers the provider build, passing the pact URL as a parameter. The provider can then verify just the changed pact without reconfiguring selectors.

### Dynamic variable substitution in webhook bodies

```
${pactbroker.consumerName}
${pactbroker.providerName}
${pactbroker.pactUrl}
${pactbroker.verificationResultUrl}
${pactbroker.consumerVersionNumber}
${pactbroker.providerVersionNumber}
${pactbroker.consumerVersionTags}
${pactbroker.consumerVersionBranch}
${pactbroker.providerVersionBranch}
```

### Managing webhooks via MCP

```
# Create a webhook
contract-testing_create_webhook
  description: "Trigger provider CI when pact changes"
  events: ["contract_requiring_verification_published"]
  consumer: "FrontendApp"    # optional: scope to specific consumer
  provider: "UserService"    # optional: scope to specific provider
  request:
    method: "POST"
    url: "https://api.github.com/repos/example/provider/actions/workflows/pact.yml/dispatches"
    headers: { "Authorization": "Bearer ${pactbroker.githubToken}" }
    body: { "ref": "main", "inputs": { "pact_url": "${pactbroker.pactUrl}" } }

# Test a webhook
contract-testing_execute_webhook
  uuid: "<webhook-uuid>"
```

### Webhook template library

See https://docs.pact.io/pact_broker/webhooks/template_library for ready-made templates for:

- GitHub Actions
- CircleCI
- GitLab CI
- Jenkins
- Travis CI
- Azure DevOps


path: references/pact-implementations.md
content: # Pact Language Implementations

> Sources: https://docs.pact.io/implementation_guides/overview.md, https://docs.pact.io/implementation_guides/javascript/docs/consumer.md, https://docs.pact.io/implementation_guides/javascript/docs/provider.md, https://docs.pact.io/implementation_guides/go/docs/consumer.md, https://docs.pact.io/implementation_guides/go/docs/provider.md, https://docs.pact.io/implementation_guides/jvm/consumer.md, https://docs.pact.io/implementation_guides/jvm/provider.md, https://docs.pact.io/implementation_guides/ruby/creating_pacts.md, https://docs.pact.io/implementation_guides/ruby/verifying_pacts.md

**DSL references** (language-specific syntax, matchers, and patterns):
[dsl.javascript.md](dsl.javascript.md) · [dsl.typescript.md](dsl.typescript.md) · [dsl.java.md](dsl.java.md) · [dsl.kotlin.md](dsl.kotlin.md) · [dsl.golang.md](dsl.golang.md) · [dsl.dotnet.md](dsl.dotnet.md) · [dsl.php.md](dsl.php.md) · [dsl.swift.md](dsl.swift.md)

---

## Language Support Matrix

| Language       | Repository                                                            | Pact Spec Support            |
| -------------- | --------------------------------------------------------------------- | ---------------------------- |
| **JavaScript** | [`pact-js`](https://github.com/pact-foundation/pact-js)               | V1, V2, V3, V4               |
| **Java / JVM** | [`pact-jvm`](https://github.com/pact-foundation/pact-jvm)             | V1, V2, V3, V4               |
| **Go**         | [`pact-go`](https://github.com/pact-foundation/pact-go)               | V1, V2, V3, V4               |
| **Ruby**       | [`pact-ruby`](https://github.com/pact-foundation/pact-ruby)           | V1, V2, V3, V4               |
| **.NET**       | [`pact-net`](https://github.com/pact-foundation/pact-net)             | V1, V2, V3, V4               |
| **Python**     | [`pact-python`](https://github.com/pact-foundation/pact-python)       | V1, V2, V3 (beta), V4 (beta) |
| **PHP**        | [`pact-php`](https://github.com/pact-foundation/pact-php)             | V1, V2, V3, V4               |
| **Rust**       | [`pact-reference`](https://github.com/pact-foundation/pact-reference) | V1, V2, V3, V4               |
| **Swift**      | [`PactSwift`](https://github.com/surpher/PactSwift)                   | V3, V4                       |
| **C++**        | [`pact-cplusplus`](https://github.com/pact-foundation/pact-cplusplus) | V3                           |
| **Scala**      | [`pact4s`](https://github.com/jbwheatley/pact4s)                      | V3, V4                       |

> Most implementations wrap the Rust FFI (`pact_ffi`) under the hood for spec V3/V4 support.

---

## JavaScript (pact-js)

### Consumer Tests

```typescript
import {
  Pact,
  Matchers,
  SpecificationVersion,
  LogLevel,
} from "@pact-foundation/pact";

const { like, eachLike } = Matchers;

// `Pact` is aliased to `PactV4` — use this for all new projects
const provider = new Pact({
  dir: path.resolve(process.cwd(), "pacts"),
  consumer: "MyConsumer",
  provider: "MyProvider",
  spec: SpecificationVersion.SPECIFICATION_VERSION_V4,
  logLevel: "debug" as LogLevel,
});

describe("My API client", () => {
  it("gets a list of products", async () => {
    await provider
      .addInteraction()
      .given("products exist")
      .uponReceiving("a request for all products")
      .withRequest("GET", "/products", (builder) => {
        builder.headers({ Accept: "application/json" });
      })
      .willRespondWith(200, (builder) => {
        builder.headers({ "Content-Type": "application/json" });
        builder.jsonBody(
          eachLike({
            id: like(1),
            name: like("Widget"),
          })
        );
      })
      .executeTest(async (mockserver) => {
        const client = new ProductsClient(mockserver.url);
        const products = await client.getProducts();
        expect(products).to.have.length.greaterThan(0);
      });
  });
});
```

**Version selection**: `Pact` is aliased to `PactV4` — always use it for new projects. Pass `spec: SpecificationVersion.SPECIFICATION_VERSION_V4` explicitly. `PactV2` and `PactV3` exports are available only for legacy code.

**V4 builder pattern**: `withRequest(method, path, builder?)` and `willRespondWith(status, builder?)` take optional builder callbacks instead of plain objects — use `builder.headers()`, `builder.jsonBody()`, `builder.query()`, etc.

**Interaction metadata (V4)**: mark interactions as `pending()`, add `comment()`, or set `testName()` for advanced lifecycle control.

### Provider Verification (JS)

```javascript
import { Verifier } from "@pact-foundation/pact";

new Verifier({
  providerBaseUrl: "http://localhost:3000",
  pactBrokerUrl: process.env.PACT_BROKER_BASE_URL,
  pactBrokerToken: process.env.PACT_BROKER_TOKEN,
  provider: "MyProvider",
  providerVersion: process.env.GIT_COMMIT,
  providerVersionBranch: process.env.GIT_BRANCH,
  consumerVersionSelectors: [
    { mainBranch: true },
    { matchingBranch: true },
    { deployedOrReleased: true },
  ],
  enablePending: true,
  includeWipPactsSince:
    process.env.GIT_BRANCH === "main" ? "2020-01-01" : undefined,
  publishVerificationResult: process.env.CI === "true",
  stateHandlers: {
    "products exist": async () => {
      await productRepository.seed([{ id: 1, name: "Widget" }]);
    },
    "no products exist": async () => {
      await productRepository.clear();
    },
  },
  requestFilter: (req, res, next) => {
    req.headers["Authorization"] = `Bearer ${generateToken()}`;
    next();
  },
}).verifyProvider();
```

**Provider verification lifecycle**: `beforeEach` → State handler → `requestFilter (request)` → Execute test → `requestFilter (response)` → `afterEach`

---

## Go (pact-go)

### Consumer Tests

```go
import (
  "github.com/pact-foundation/pact-go/v2/consumer"
  "github.com/pact-foundation/pact-go/v2/matchers"
)

func TestProductsClient(t *testing.T) {
  mockProvider, err := consumer.NewV3Pact(consumer.MockHTTPProviderConfig{
    Consumer: "ProductsConsumer",
    Provider: "ProductsProvider",
  })
  assert.NoError(t, err)

  mockProvider.
    AddInteraction().
    Given("products exist").
    UponReceiving("a request for all products").
    WithRequest("GET", matchers.S("/products")).
    WillRespondWith(200).
    WithBodyMatch(&[]Product{})  // auto-generates matchers from struct

  err = mockProvider.ExecuteTest(t, func(config consumer.MockServerConfig) error {
    client := NewProductsClient(fmt.Sprintf("http://%s:%d", config.Host, config.Port))
    products, err := client.GetProducts()
    assert.NoError(t, err)
    assert.Greater(t, len(products), 0)
    return err
  })
  assert.NoError(t, err)
}
```

**Matchers** (Go):

| Matcher                            | Min Spec | Description              |
| ---------------------------------- | -------- | ------------------------ |
| `Like(value)`                      | V2       | Type match               |
| `Term(example, regex)`             | V2       | Regex match              |
| `EachLike(value, min)`             | V2       | Array of type            |
| `Equality(value)`                  | V3       | Exact value              |
| `ArrayContaining(variants)`        | V3       | Heterogeneous arrays     |
| `FromProviderState(expr, example)` | V3       | Dynamic injection        |
| `DateGenerated` / `TimeGenerated`  | V3       | Cross-platform date/time |

**Auto-generate from struct tags**:

```go
type Product struct {
  ID   string `json:"id"`
  Name string `json:"name"`
  Tags []string `json:"tags" pact:"min=2"`
  Date string `json:"date" pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"`
}
// Use WithBodyMatch(&Product{}) in the interaction
```

### Provider Verification (Go)

```go
func TestProvider(t *testing.T) {
  go startServer()

  verifier := provider.HTTPVerifier{}
  err := verifier.VerifyProvider(t, provider.VerifyRequest{
    ProviderBaseURL: "http://localhost:1234",
    Provider:        "ProductsProvider",
    ProviderVersion: os.Getenv("APP_SHA"),
    ProviderVersionBranch: os.Getenv("GIT_BRANCH"),
    BrokerURL:       os.Getenv("PACT_BROKER_BASE_URL"),
    BrokerToken:     os.Getenv("PACT_BROKER_TOKEN"),
    ConsumerVersionSelectors: []provider.Selector{
      &provider.ConsumerVersionSelector{MainBranch: true},
      &provider.ConsumerVersionSelector{MatchingBranch: true},
      &provider.ConsumerVersionSelector{DeployedOrReleased: true},
    },
    EnablePending:              true,
    IncludeWIPPactsSince:       wipSince,
    PublishVerificationResults: os.Getenv("CI") == "true",
    StateHandlers: provider.StateHandlers{
      "products exist": func(setup bool, s provider.ProviderStateV3) (provider.ProviderStateV3Response, error) {
        if setup {
          productRepo.Seed([]Product{{ID: "1", Name: "Widget"}})
        }
        return nil, nil
      },
    },
    RequestFilter: func(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", getToken()))
        next.ServeHTTP(w, r)
      })
    },
  })
  assert.NoError(t, err)
}
```

**State handler setup/teardown**: The `setup bool` parameter is `true` before the interaction, `false` after. Use it to set up and tear down test data in the same handler.

---

## Java / JVM (pact-jvm)

### Consumer Tests (JUnit 5)

```java
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(providerName = "ProductsProvider")
class ProductsConsumerTest {

  @Pact(consumer = "ProductsConsumer")
  RequestResponsePact productsPact(PactDslWithProvider builder) {
    return builder
      .given("products exist")
      .uponReceiving("a request for all products")
        .method("GET")
        .path("/products")
      .willRespondWith()
        .status(200)
        .body(LambdaDsl.newJsonArrayMinLike(1, array ->
          array.object(obj -> {
            obj.numberType("id");
            obj.stringType("name", "Widget");
            obj.stringMatcher("type", "^[A-Z]+$", "PRODUCT");
          })
        ).build())
      .toPact();
  }

  @Test
  @PactTestFor(pactMethod = "productsPact")
  void verifyProductsClient(MockServer mockServer) {
    ProductsClient client = new ProductsClient(mockServer.getUrl());
    List<Product> products = client.getProducts();
    assertThat(products).isNotEmpty();
  }
}
```

**JUnit 4** uses `PactProviderRuleMk2` instead of the JUnit5 extension.

### Provider Verification (JUnit 5)

```java
@Provider("ProductsProvider")
@PactBroker(
  host = "${PACT_BROKER_HOST}",
  authentication = @PactBrokerAuth([REDACTED_SECRET]")
)
class ProductsProviderTest {

  @TestTemplate
  @ExtendWith(PactVerificationInvocationContextProvider.class)
  void verifyPact(PactVerificationContext context) {
    context.verifyInteraction();
  }

  @BeforeEach
  void before(PactVerificationContext context) {
    context.setTarget(HttpTestTarget.fromUrl(new URL("http://localhost:" + port)));
  }

  @State("products exist")
  void productsExist() {
    productRepository.save(new Product(1L, "Widget", "PRODUCT"));
  }

  @State("no products exist")
  void noProductsExist() {
    productRepository.deleteAll();
  }
}
```

**System properties for publishing**:

```
-Dpact.provider.version=$GIT_COMMIT
-Dpact.provider.branch=$GIT_BRANCH
-Dpact.verifier.publishResults=true
```

---

## Ruby (pact-ruby)

### Consumer Tests

```ruby
require 'pact/consumer/rspec'

Pact.service_consumer "ProductsConsumer" do
  has_pact_with "ProductsProvider" do
    mock_service :products_service do
      port 1234
    end
  end
end

describe ProductsClient do
  before do
    products_service.upon_receiving("a request for all products")
      .with(method: :get, path: '/products')
      .will_respond_with(
        status: 200,
        headers: { 'Content-Type' => 'application/json' },
        body: Pact.each_like({
          id: Pact.like(1),
          name: Pact.like('Widget'),
          type: Pact.term(generate: 'PRODUCT', matcher: /^[A-Z]+$/)
        })
      )
  end

  it 'returns a list of products' do
    products = subject.get_products
    expect(products).not_to be_empty
  end
end
```

### Provider Verification (Ruby)

```ruby
# spec/service_consumers/pact_helper.rb
require 'pact/provider/rspec'

Pact.service_provider "ProductsProvider" do
  app_version ENV['GIT_COMMIT']
  app_version_branch ENV['GIT_BRANCH']
  publish_verification_results ENV['CI'] == 'true'

  honours_pacts_from_pact_broker do
    pact_broker_base_url 'https://yourorg.pactflow.io',
      { token: ENV['PACT_BROKER_TOKEN'] }

    consumer_version_selectors [
      { main_branch: true },
      { matching_branch: true },
      { deployed_or_released: true }
    ]

    enable_pending true
    include_wip_pacts_since ENV['GIT_BRANCH'] == 'main' ? '2020-01-01' : nil
  end
end

# Provider states
Pact.provider_states_for "ProductsConsumer" do
  provider_state "products exist" do
    set_up { Product.create!(id: 1, name: 'Widget', type: 'PRODUCT') }
    tear_down { Product.delete_all }
  end
end
```

---

## CLI Tooling

Language-agnostic CLI tools available for all Pact implementations:

| Tool                                 | Use                                                         |
| ------------------------------------ | ----------------------------------------------------------- |
| `pact-broker publish`                | Publish pact files to Pact Broker                           |
| `pact-broker can-i-deploy`           | Check deployment safety                                     |
| `pact-broker record-deployment`      | Record deployment to environment                            |
| `pact-broker record-release`         | Record a release (mobile/libraries)                         |
| `pact-broker create-environment`     | Set up environments                                         |
| `pactflow publish-provider-contract` | Publish OpenAPI spec for BDCT                               |
| `pact-stub-server`                   | Serve pact files as HTTP stubs                              |
| `pact-verifier`                      | Verify pacts against a running provider (language-agnostic) |

Install via (see [docs.pact.io/implementation_guides/cli](https://docs.pact.io/implementation_guides/cli#-installation-methods) for full details):

```bash
# Linux/macOS/WSL (recommended: Unified CLI)
curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-cli/main/install.sh | sh

# Homebrew (macOS/Linux)
brew tap pact-foundation/tap
brew install pact-foundation/tap/pact

# Windows (PowerShell)
iwr -useb https://raw.githubusercontent.com/pact-foundation/pact-cli/main/install.ps1 | iex

# Windows (Scoop)
scoop bucket add pact https://github.com/pact-foundation/scoop
scoop install pact

# Cargo (Rust)
cargo install pact

# Docker
docker run --rm -it pactfoundation/pact:latest
# or via GitHub Container Registry
docker run --rm -it ghcr.io/pact-foundation/pact:latest
```

```yaml
# GitHub Actions
- uses: pact-foundation/pact-cli@main
- name: Run Pact CLI
  run: pact --help
```

See `references/pact-broker-setup.md` for full CLI command reference.


path: references/dsl.swift.md
content: While you already know this, here is a reminder of the key PactSwift classes, types, structs and methods you will need to use to create a Pact test in Swift (having omitted deprecated and unadvised methods):

File: MockService.swift
```
open class MockService
	public convenience init(
		consumer: String,
		provider: String,
		scheme: TransferProtocol = .standard,
		writePactTo directory: URL? = nil,
		merge: Bool = true
	)
	public func uponReceiving(_ description: String) -> Interaction
	public func run(
		_ file: FileString? = #file,
		line: UInt? = #line,
		verify interactions: [Interaction]? = nil,
		timeout: TimeInterval? = nil,
		testFunction: @escaping (_ baseURL: String, _ done: (@escaping @Sendable () -> Void)) throws -> Void
	)
```

File: MockService+Concurrency.swift
```
extension MockService {
	public func run(
		verify interactions: [Interaction]? = nil,
		timeout: TimeInterval? = nil,
		testFunction: @escaping (_ baseURL: String) async throws -> Void
	)
}
```

File: MockService+Extension.swift
```
extension MockService
	public func given(_ providerState: String) -> Interaction
```

File: Model/Interaction.swift
```
public class Interaction
	public init(description: String, providerStates: [ProviderState], request: Request, response: Response)
```

File: Model/PactHTTPMethod.swift
```
public enum PactHTTPMethod: String {
	case GET
	case POST
	case PUT
	case DELETE
	case PATCH
	case HEAD
	case OPTIONS
}
```

File: Model/ProviderState.swift
```
public struct ProviderState
	public init(description: String, parameters: [String: Any]? = nil)
```

File: Model/Request.swift
```
public struct Request
	public init(method: PactHTTPMethod, path: String, query: [String: String]? = nil, headers: [String: String]? = nil, body: Any? = nil)
```

File: Model/Response.swift
```
public struct Response
	public init(status: Int, headers: [String: String]? = nil, body: Any? = nil)
```

I'll also include a summary of the generator and matcher classes (omitting the file location and namespace for brevity):

```
public struct FromProviderState: Matcher
	public init(_ expression: String, example: Any)
public struct OneOf: Matcher
	public init(_ variants: [Any])
public struct SomethingLike: Matcher
	public init(_ value: Any)
public struct EqualTo: Matcher
	public init(_ value: Any)
public struct EachLike: Matcher
	public init(_ value: Any, min: Int = 1, max: Int? = nil)
public struct EachKeyLike: Matcher
	public init(_ key: String, value: Any)
public struct IntegerLike: Matcher
	public init(_ value: Int)
public struct RegexLike: Matcher
	public init(_ pattern: String, example: String)
public struct IncludesLike: Matcher
	public init(_ value: String)
public struct DecimalLike: Matcher
	public init(_ value: Decimal)
public struct MatchNull: Matcher
	public init()
public struct RandomBool: ExampleGenerator
	public init()
public struct RandomString: ExampleGenerator
	public init(size: Int)
public struct RandomDateTime: ExampleGenerator
	public init()
public struct RandomDecimal: ExampleGenerator
	public init()
public struct RandomHexadecimal: ExampleGenerator
	public init(size: Int)
public struct RandomInt: ExampleGenerator
	public init(min: Int, max: Int)
public struct DateTimeExpression: ExampleGenerator
	public init(_ expression: String)
public struct RandomTime: ExampleGenerator
	public init()
public struct RandomUUID: ExampleGenerator
	public init()
public struct RandomDate: ExampleGenerator
	public init()
public struct ProviderStateGenerator: ExampleGenerator
	public init(_ expression: String)
public struct DateTime: ExampleGenerator
	public init(format: String)
```


path: references/pact-recipes.md
content: # Pact Recipes & Common Patterns

> Sources: https://docs.pact.io/recipes.md, https://docs.pact.io/recipes/graphql.md, https://docs.pact.io/recipes/kafka.md, https://docs.pact.io/recipes/optional.md, https://docs.pact.io/consumer/using_pact_to_support_ui_testing.md

---

## Recipe Index

| Pattern                                          | Description                                              |
| ------------------------------------------------ | -------------------------------------------------------- |
| [Optional Fields](#optional-fields)              | Testing payloads where some fields may be absent or null |
| [GraphQL](#graphql)                              | Strategies for testing GraphQL endpoints                 |
| [Kafka / Message Queues](#kafka--message-queues) | Contract testing for async message-based APIs            |
| [API Gateway](#api-gateway)                      | Dealing with AWS API Gateway, Kong, and similar          |
| [AWS Signed Requests](#aws-signed-requests)      | Testing services that use AWS Signature V4               |
| [Cypress Integration](#cypress-integration)      | Combining Pact with Cypress UI tests                     |
| [UI Testing with Pact](#ui-testing-with-pact)    | Using pact stubs as fixtures for UI tests                |

---

## Optional Fields

> Source: https://docs.pact.io/recipes/optional.md

Optional values in API responses are a common challenge because Pact has no "optional" matcher. There is no `stringOrNull(...)` — you cannot define something like this inline.

### Solution 1: Provider States

Define separate interactions for each variant using different provider states.

```javascript
// Consumer: two interactions, one per variant
it("returns a user with lastLogin", async () => {
  await pact
    .addInteraction()
    .given("user 1 has previously logged in")
    .uponReceiving("a request to get a user")
    .withRequest("GET", "/users/1")
    .willRespondWith(200, (builder) => {
      builder.jsonBody({ id: "1", lastLogin: "2025-10-20" });
    })
    .executeTest(async (mockserver) => {
      const user = await client.getUser(1);
      expect(user.lastLogin).to.exist;
    });
});

it("returns a user without lastLogin", async () => {
  await pact
    .addInteraction()
    .given("user 1 has not previously logged in")
    .uponReceiving("a request to get a user")
    .withRequest("GET", "/users/1")
    .willRespondWith(200, (builder) => {
      builder.jsonBody({ id: "1" });
    })
    .executeTest(async (mockserver) => {
      const user = await client.getUser(1);
      expect(user.lastLogin).to.be.undefined;
    });
});

// Provider: state handlers for each variant
const stateHandlers = {
  "user 1 has previously logged in": () => {
    userRepository.set("1", new User("Bill", "2025-10-20"));
  },
  "user 1 has not previously logged in": () => {
    userRepository.set("1", new User("Bill", null));
  },
};
```

### Solution 2: Array Contains Matcher (Pact V4)

When working with lists of objects with different shapes, the Array Contains matcher verifies that all expected variants appear in the provider response. Extra objects in the response are ignored.

```javascript
// Consumer
await pact
  .addInteraction()
  .uponReceiving("a request for users with various states")
  .withRequest("GET", "/users")
  .willRespondWith(200, (builder) => {
    builder.jsonBody({
      users: Matchers.arrayContaining(
        Matchers.like({ id: "123", lastLogin: null }),
        Matchers.like({ id: "456", lastLogin: "2025-10-20" }),
      ),
    });
  })
  .executeTest(async (mockserver) => {
    const users = await client.getUsers();
    // Consumer can handle both shapes
  });
```

**Guidance**:

- Use Provider States when you can easily control data setup per scenario
- Use Array Contains for collections with varying shapes
- Test "minimum" and "maximum" object shapes — don't test every combination

---

## GraphQL

> Source: https://docs.pact.io/recipes/graphql.md

GraphQL is just HTTP, so you can use Pact to test it like any other HTTP API.

- **Query string GraphQL**: works out of the gate — use standard HTTP matching
- **POST-based GraphQL**: create a utility function to wrap requests and reduce boilerplate

Some languages have GraphQL helpers:

- **Pact JS**: `import { GraphQLInteraction } from '@pact-foundation/pact'`
- **Pact Go**: GraphQL helper available via PR

### Tips for GraphQL testing

- Match on the `query` field in the request body using type matching (not exact) — query whitespace and formatting can vary
- Use regex on the query string if you need to pin the operation name
- Response body: use type/structure matching rather than exact values

### Examples

- Pact JS: https://github.com/pact-foundation/pact-js/tree/master/examples/graphql
- Pact Go: https://github.com/pact-foundation/pact-go/blob/v1.x.x/examples/graphql/consumer/graphql_consumer_test.go

---

## Kafka / Message Queues

See `references/pact-messages.md` for full Kafka consumer/provider examples with Java/JVM.

**Key points**:

- Pact abstracts away the transport — it focuses on message content
- Use `ProviderType.ASYNCH` in JVM tests
- Use `providerWithMetadata` in JS tests
- Set `contentType` in metadata to control serialization behavior
- For Confluent Schema Registry: use `application/vnd.schemaregistry.v1+json` contentType — Pact handles the 5 magic bytes automatically

---

## API Gateway

When your provider sits behind an API Gateway (AWS API Gateway, Kong, Nginx), you have two choices:

### Option 1: Bypass the gateway in tests

Point the provider verification directly at the application server (not the gateway). The gateway-level concerns (auth, routing, rate limiting) are tested separately.

This is the recommended approach. The pact only covers what the consumer's API client sends and receives. Gateway concerns are orthogonal.

### Option 2: Include the gateway

If the gateway transforms requests/responses (e.g. adds JWT claims, translates paths), you may need to include it. Use a `requestFilter` on the provider side to inject auth headers, or set up a local gateway proxy for tests.

**Warning**: including the gateway in pact tests couples your tests to infrastructure. Only do this if transformations genuinely affect the contract.

---

## AWS Signed Requests

For services that use [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html):

Use a **request filter** on the provider side to sign requests before they're sent to the provider. This is the same approach as auth token injection:

```javascript
requestFilter: (req, res, next) => {
  // Sign the request using aws4 or similar library
  const signedHeaders = signRequest(req);
  Object.assign(req.headers, signedHeaders);
  next();
};
```

On the consumer side, don't worry about the signing in the pact test — you're testing the payload structure, not the auth mechanism.

---

## Cypress Integration

> Source: https://docs.pact.io/recipes/cypress.md

**Best practice**: Don't use Pact and Cypress together for the same tests. Instead:

1. Run Pact consumer tests to generate pact files
2. Use the generated pact files (or shared fixtures) as HTTP stubs in your Cypress tests

This avoids:

- Hard-to-debug tests with multiple simultaneous mock interactions
- Multiple redundant calls with slight variations
- Coupling UI test setup to Pact library internals

### Converting Cypress stubs to Pact

Libraries like [`msw-pact`](https://github.com/you54f/msw-pact) can convert your `cy.route()` / `cy.intercept()` calls into pact files — giving you a pact contract without rewriting your Cypress tests.

PactFlow also has [dedicated Cypress + Pact examples](https://docs.pactflow.io/docs/examples/cypress/readme) showing how to safely combine the two.

---

## UI Testing with Pact

> Source: https://docs.pact.io/consumer/using_pact_to_support_ui_testing.md

The generated pact file is a JSON document describing all the HTTP interactions your consumer depends on. You can use it as a source of truth for HTTP stubs in UI tests:

1. Run Pact consumer tests → generates `pact.json`
2. Serve the pact as a stub using the [Pact Stub Server](https://github.com/pact-foundation/pact-stub-server):
   ```bash
   pact-stub-server --file ./pacts/consumer-provider.json --port 8080
   ```
3. Point your UI tests at `http://localhost:8080`

This gives you:

- UI tests that use the same fixtures as your contract tests (no drift)
- HTTP stubs that are guaranteed to match what the provider will actually return
- Fast, hermetic UI tests with no real backend

---

## NestJS

Use [`nestjs-pact`](https://github.com/omermorad/nestjs-pact) for first-class NestJS integration.

## Jest

Use [`jest-pact`](https://github.com/pact-foundation/jest-pact) — wraps Pact's DSL with Jest lifecycle hooks for cleaner test setup.

## Mocha

Use [`mocha-pact`](https://github.com/pact-foundation/mocha-pact) — same idea for Mocha.

## MSW (Mock Service Worker)

Use [`msw-pact`](https://github.com/you54f/msw-pact) to convert MSW handlers into pact files.


path: references/pact-docs-index.md
content: # Pact Documentation Index

> Source: https://docs.pact.io/llms.txt — Fast and reliable testing for your APIs and microservices during development. Safety during deployment.

All pages below are available at `https://docs.pact.io/{path}` and as clean markdown at `https://docs.pact.io/{path}.md`.

---

## Getting Started

- [5 minute guide](/5-minute-getting-started-guide.md): From zero to running Pact tests in 5 mins
- [Conceptual Overview](/getting_started/conceptual_overview.md): Using Pact successfully requires you to understand how Pact works and the concepts and terminology behind Pact.
- [Terminology](/getting_started/terminology.md): Service Consumer, Service Provider, Mock Service Provider, Interaction, Pact file, Pact verification, Provider state, Pact specification, Pact Broker, PactFlow, The Matrix, Can-I-Deploy, Pacticipant.
- [How Pact works](/getting_started/how_pact_works.md): Consumer testing, provider verification, putting it all together. Also covers Message Pact for async systems.
- [Matching](/getting_started/matching.md): Regular expressions, type matching, flexible length arrays, query params.
- [Gotchas](/getting_started/matching/gotchas.md): Pact follows Postel's law — be liberal in what you accept.
- [Provider states](/getting_started/provider_states.md): Preconditions for test scenarios. Analog of `Given` in Cucumber.
- [Provider verification](/getting_started/provider_verification.md): What is it? How to run it. Best practices.
- [Sharing Pacts with the Pact Broker](/getting_started/sharing_pacts.md): Consumer CI generates pact files; provider CI generates verification results. These artifacts must be shared.
- [Pact Specification](/getting_started/specification.md): V1–V4 specification versions and language support.
- [Local API stubs](/getting_started/stubs.md): Use Pact contracts as locally running API stubs.
- [Testing scope](/getting_started/testing-scope.md): What scope should Pact tests cover?
- [Verifying Pacts](/getting_started/verifying_pacts.md): Replaying requests from pact files against the provider.
- [Versioning in the Pact Broker](/getting_started/versioning_in_the_pact_broker.md): How application version numbers should be set. Best practices.
- [When to use Pact](/getting_started/what_is_pact_good_for.md): Good fits and bad fits for contract testing.
- [Comparisons with other tools](/getting_started/comparisons.md): How Pact differs from Spring Cloud Contract, Swagger, etc.
- [Further reading](/getting_started/further_reading.md): Talks, presentations, books.

---

## Consumer Guide

- [Writing Consumer tests](/consumer.md): The Golden Rule. Focus on the messages, not the behaviour. Use matchers. Avoid UI testing. Isolated unit tests only.
- [Contract Tests vs Functional Tests](/consumer/contract_tests_not_functional_tests.md): Contract tests check messages, not side effects. Don't use Pact to test business logic.
- [Recommended configuration for publishing pacts](/consumer/recommended_configuration.md): Branch-based workflow. Always publish with a branch name. Tag with environment after deploy.
- [Using Pact to support UI testing](/consumer/using_pact_to_support_ui_testing.md): Use generated pacts as HTTP stubs for UI tests, not the other way around.

---

## Provider Guide

- [Verifying Pacts](/provider.md): Run against a locally running provider. Stub downstream dependencies. Publish results to broker.
- [Handling authentication and authorization](/provider/handling_auth.md): Stub auth services, use hardcoded credentials, provider states, or request filters.
- [Help! My pact verification tests are failing](/provider/how_to_fix_failing_verification_tests.md): Step-by-step debug guide for Ruby/JS/Python/Go/Swift and JVM.
- [Recommended configuration for verifying pacts](/provider/recommended_configuration.md): Consumer version selectors, pending pacts, WIP pacts. Full JS and Ruby examples.
- [Using provider states effectively](/provider/using_provider_states_effectively.md): Avoid false positives. Echo query params in responses. Use multiple data points in provider states.

---

## Pact Broker

- [Introduction](/pact_broker.md): Application for sharing consumer driven contracts and verification results.
- [Overview](/pact_broker/overview.md): How the Pact Broker supports contract testing.
- [Quick start guide](/pact_broker/quick_start.md)
- [Set up Checklist](/pact_broker/set_up_checklist.md): High-level checklist for setting up an integration.
- [Versioning in the Pact Broker](/pact_broker/pacticipant_version_numbers.md): Version numbers, how pact content is versioned behind the scenes.
- [Publishing and retrieving pacts](/pact_broker/publishing_and_retrieving_pacts.md)
- [Branches](/pact_broker/branches.md): First-class branch support from v2.82.0. Supersedes tags for branch tracking.
- [Recording deployments and releases](/pact_broker/recording_deployments_and_releases.md): `record-deployment` vs `record-release`. When to use each. Migration from tags.
- [Tags](/pact_broker/tags.md): Legacy; superseded by branches and environments. Still supported.
- [Can I Deploy](/pact_broker/can_i_deploy.md): The Pact Matrix. CLI usage. Exit codes. Examples. Tags vs environments.
- [Webhooks](/pact_broker/webhooks.md): Trigger provider builds when pacts change. Events: `contract_published`, `contract_requiring_verification_published`, `verification_published`.
- [Debugging webhooks](/pact_broker/webhooks/debugging_webhooks.md)
- [Dynamic variable substitution](/pact_broker/webhooks/dynamic_variable_sub.md): `${pactbroker.consumerName}`, `${pactbroker.providerName}`, etc.
- [Webhooks template library](/pact_broker/webhooks/template_library.md): GitHub Actions, CircleCI, GitLab, Jenkins, Travis CI examples.
- [Terraform Provider](/pact_broker/terraform_provider.md): Automate Pact Broker configuration with Terraform.
- [Configuration settings](/pact_broker/configuration/settings.md): Full list of Pact Broker config options.
- [Docker images](/pact_broker/docker_images.md): `pactfoundation/pact-broker` on Dockerhub.
- [Kubernetes / Helm](/pact_broker/kubernetes.md): Deploy with Helm.
- [Administration](/pact_broker/administration.md): HAL Browser, deleting resources, maintenance.
- [Changelog](/pact_broker/changelog.md)

### Pact Broker — Advanced Topics

- [Consumer Version Selectors](/pact_broker/advanced_topics/consumer_version_selectors.md): `mainBranch`, `branch`, `deployedOrReleased`, `deployed`, `released`, `matchingBranch`, `environment`, `tag`, `latest`, `consumer`. Recommended and advanced examples.
- [Pending pacts](/pact_broker/advanced_topics/pending_pacts.md): Protects provider builds from new consumer interactions. Enable with `enablePending: true`.
- [Work In Progress pacts](/pact_broker/advanced_topics/wip_pacts.md): Auto-verifies new pacts in the provider's main pipeline. Enable with `includeWipPactsSince`.
- [Matrix selectors](/pact_broker/advanced_topics/matrix_selectors.md): Advanced query syntax for the verification matrix.
- [Provider verification badges](/pact_broker/advanced_topics/provider_verification_badges.md)
- [Provider verification results](/pact_broker/advanced_topics/provider_verification_results.md): Publishing results back to the broker.
- [How to see what has changed in a pact](/pact_broker/advanced_topics/see_changes_pact.md)
- [Using TLS](/pact_broker/advanced_topics/using-tls.md)
- [Troubleshooting](/pact_broker/advanced_topics/troubleshooting.md)

### Pact Broker — API Docs

- [Pacticipants API](/pact_broker/api/pacticipants.md)
- [Webhooks API](/pact_broker/api/webhooks.md)
- [Pagination](/pact_broker/api/pagination.md)
- [Publishing pacts API](/pact_broker/advanced_topics/api_docs/publish_pact.md)
- [Publishing verification results API](/pact_broker/advanced_topics/api_docs/publish_verification_result.md)

---

## Pact Nirvana — CI/CD Setup Guide

The progressive steps to full contract testing in CI/CD:

- [Overview](/pact_nirvana.md): "The steps for reaching Pact Nirvana". Independent deployments with confidence.
- [Step 1: Learn about Pact](/pact_nirvana/step_1.md): Prerequisites.
- [Step 2: Talk — get team alignment](/pact_nirvana/step_2.md): Consumer and provider teams must coordinate.
- [Step 3: Bronze — single test working manually](/pact_nirvana/step_3.md): Consumer test → pact file → provider verification locally.
- [Step 4: Silver — manually integrate with Pact Broker](/pact_nirvana/step_4.md): Publish pacts to broker. Verify via URL. Consumer version selectors.
- [Step 5: Gold — integrate with PR pipelines](/pact_nirvana/step_5.md): Run consumer tests in CI. Publish pacts. Provider verifies in CI.
- [Step 6: Platinum — can-i-deploy with branch to PR pipelines](/pact_nirvana/step_6.md): Add `can-i-deploy` gate. Add webhook to trigger provider build on pact change. Record deployments.
- [Step 7: Diamond — independent deployments](/pact_nirvana/step_7.md): Verify against deployed versions. Deploy consumer and provider independently with confidence.
- [Universal CI/CD pipeline notes](/pact_nirvana/notes_1.md): Handling `can-i-deploy` in shared pipelines where not all services use Pact.

---

## Recipes — Common Scenarios

- [Overview](/recipes.md)
- [API Gateway](/recipes/apigateway.md)
- [AWS API Gateway Signed Requests](/recipes/awssignedrequests.md)
- [Cypress](/recipes/cypress.md): Use pact contracts as HTTP stubs for Cypress tests.
- [GraphQL](/recipes/graphql.md): GraphQL is an HTTP abstraction — test via Pact HTTP.
- [Kafka](/recipes/kafka.md): Message pacts for Kafka.
- [Lambda (async)](/recipes/lambdaasync.md): Async Lambda and FaaS testing.
- [Lambda (HTTP)](/recipes/lambdahttp.md): HTTP-based Lambda with AWS SAM.
- [Optional Fields](/recipes/optional.md): Strategies for handling optional response fields.

---

## Implementation Guides — CLI Tools

- [Pact Broker Client CLI](/implementation_guides/cli/pact-broker-cli.md): `pact-broker` CLI — can-i-deploy, record-deployment, record-release, publish pacts, webhooks.
- [Pact CLI](/implementation_guides/cli/pact-cli.md): Combined CLI image.
- [Pact Mock Server CLI](/implementation_guides/cli/pact-mock-server.md)
- [Pact Stub Server](/implementation_guides/cli/pact-stub-server.md): Run a stub server from a pact file.
- [Pact Verifier CLI](/implementation_guides/cli/pact-verifier.md)
- [Pact Plugin CLI](/implementation_guides/cli/pact-plugin.md)

---

## Implementation Guides — Languages

- **JavaScript/TypeScript** (pact-js): [Overview](/implementation_guides/javascript/readme.md) · [Consumer](/implementation_guides/javascript/docs/consumer.md) · [Provider](/implementation_guides/javascript/docs/provider.md) · [Matching](/implementation_guides/javascript/docs/matching.md) · [Messages](/implementation_guides/javascript/docs/messages.md) · [GraphQL](/implementation_guides/javascript/docs/graphql.md) · [Plugins](/implementation_guides/javascript/docs/plugins.md) · [Troubleshooting](/implementation_guides/javascript/docs/troubleshooting.md)
- **Java/JVM** (pact-jvm): [Overview](/implementation_guides/jvm.md) · [Consumer](/implementation_guides/jvm/consumer.md) · [Provider](/implementation_guides/jvm/provider.md) · [Matching](/implementation_guides/jvm/matching.md) · [Gradle](/implementation_guides/jvm/provider/gradle.md) · [Maven](/implementation_guides/jvm/provider/maven.md) · [JUnit 5](/implementation_guides/jvm/provider/junit5.md) · [Spring](/implementation_guides/jvm/provider/spring.md)
- **Go** (pact-go): [Overview](/implementation_guides/go.md) · [Consumer](/implementation_guides/go/docs/consumer.md) · [Provider](/implementation_guides/go/docs/provider.md) · [Messages](/implementation_guides/go/docs/messages.md) · [Plugins](/implementation_guides/go/docs/plugins.md) · [Troubleshooting](/implementation_guides/go/docs/troubleshooting.md)
- **Python** (pact-python): [Overview](/implementation_guides/python.md) · [Consumer](/implementation_guides/python/docs/consumer.md) · [Provider](/implementation_guides/python/docs/provider.md)
- **Ruby** (pact-ruby): [Creating pacts](/implementation_guides/ruby/creating_pacts.md) · [Verifying pacts](/implementation_guides/ruby/verifying_pacts.md) · [Matching](/implementation_guides/ruby/matching.md) · [Provider states](/implementation_guides/ruby/provider_states.md) · [Configuration](/implementation_guides/ruby/configuration.md)
- **Rust**: [Overview](/implementation_guides/rust.md) · [pact_consumer](/implementation_guides/rust/pact_consumer.md) · [pact_ffi](/implementation_guides/rust/pact_ffi.md) · [pact_verifier](/implementation_guides/rust/pact_verifier.md)
- **.NET** (PactNet): [Overview](/implementation_guides/net.md) · [Messaging](/implementation_guides/net/docs/messaging-pacts.md) · [Upgrading to v4](/implementation_guides/net/docs/upgrading-to-4.md) · [Upgrading to v5](/implementation_guides/net/docs/upgrading-to-5.md)
- **PHP**: [Consumer](/implementation_guides/php/docs/consumer.md) · [Provider](/implementation_guides/php/docs/provider.md) · [Messages](/implementation_guides/php/docs/messages.md)
- **C++**: [Consumer DSL](/implementation_guides/cpp/consumer.md)
- **Swift**: [Overview](/implementation_guides/swift.md) — v2 spec only
- **Scala**: [Overview](/implementation_guides/scala.md) — github.com/ITV/scala-pact
- [Other Languages](/implementation_guides/other_languages.md): Strategies when no Pact client exists
- [Feature toggles](/implementation_guides/feature_toggles.md): Working with configurable behavior
- [SSL/TLS](/implementation_guides/ssl.md): TLS configuration and proxy settings

---

## Pact Plugins Framework

- [Overview](/implementation_guides/pact_plugins.md): Extend Pact to support new protocols and content types.
- [Quick start](/plugins/quick_start.md)
- [Plugin directory](/plugins/directory.md)
- [Writing a plugin guide](/implementation_guides/pact_plugins/docs/writing-plugin-guide.md): Plugins are gRPC servers that run as child processes.
- [gRPC examples](/implementation_guides/pact_plugins/examples/grpc.md)
- [Protobuf examples](/implementation_guides/pact_plugins/examples/protobuf.md)
- [CSV examples](/implementation_guides/pact_plugins/examples/csv.md)

---

## Pact University — Workshops

- [Introduction to Pact workshop (JS)](/university/introduction.md): 13 steps from zero to full CI/CD setup with PactFlow.
- [Message Pact (async) workshop](/university/message-pact-async/00_1_Intro.md): Async messaging with Kafka.

---

## Community & Support

- [FAQ](/faq.md): Technical and complex questions answered.
- [Convince me](/faq/convinceme.md): How to make the case for contract testing.
- [Where to go for help](/help.md): Slack, documentation, GitHub issues.
- [SmartBear / PactFlow](/help/smartbear.md): Commercial entity behind PactFlow.
- [Community projects](/community_repos.md)
- [Case studies](/users/case_studies.md): Atlassian, Gov.UK Pay, M1 Finance, Boost Insurance.
- [Who is using Pact](/users.md)
- [History](/history.md): Pact origins at realestate.com.au in 2013.
- [Roadmap](/roadmap.md): Feature roadmap and language support matrix.
- [Feature support by language](/roadmap/feature_support.md)
- [Telemetry](/telemetry.md)
- [Contributing](/contributing.md)


path: references/pact-provider.md
content: # Pact Provider Guide

> Sources: https://docs.pact.io/provider.md, https://docs.pact.io/provider/recommended_configuration.md, https://docs.pact.io/provider/handling_auth.md, https://docs.pact.io/provider/how_to_fix_failing_verification_tests.md, https://docs.pact.io/provider/using_provider_states_effectively.md

---

## Provider Verification Fundamentals

- **Always verify against a locally running instance** of the provider, not a deployed environment. External dependencies (other services) should be stubbed.
- **Run pact:verify in CI** alongside all other tests.
- **Only stub layers below where the request body is extracted** — if you stub too high, you could send garbage in a POST body and the test would still pass.
- **Publish verification results** to the Pact Broker so consumers can see the status.

---

## Recommended Provider Configuration

> Source: https://docs.pact.io/provider/recommended_configuration.md

There are two reasons to run pact verification:

1. **Provider change**: Full regression verification against all consumers and all supported environments.
2. **Pact change**: Only the changed pact needs to be verified (triggered by webhook).

### Verification triggered by a provider change

#### Consumer Version Selectors (branches/environments — recommended)

```javascript
// JavaScript example
const verificationOptions = {
  provider: "example-provider",
  pactBrokerUrl: "http://test.pactflow.io",
  consumerVersionSelectors: [
    { mainBranch: true }, // latest from consumer's main branch
    { matchingBranch: true }, // latest from branch matching current provider branch
    { deployedOrReleased: true }, // all currently deployed/released versions
  ],
  enablePending: true,
  // Only include WIP pacts on main branch
  ...(process.env.GIT_BRANCH === "main"
    ? { includeWipPactsSince: "2020-01-01" }
    : {}),
  // Publishing verification results
  publishVerificationResult: process.env.CI === "true",
  providerVersion: process.env.GIT_COMMIT,
  providerVersionBranch: process.env.GIT_BRANCH,
};
```

```ruby
# Ruby example
Pact.service_provider "example-provider" do
  app_version ENV['GIT_COMMIT']
  app_version_branch ENV['GIT_BRANCH']
  publish_verification_results ENV['CI'] == 'true'

  honours_pacts_from_pact_broker do
    pact_broker_base_url 'http://test.pactflow.io'
    consumer_version_selectors [
      { main_branch: true },
      { matching_branch: true },
      { deployed_or_released: true }
    ]
    enable_pending true
    include_wip_pacts_since ENV['GIT_BRANCH'] == "main" ? "2020-01-01" : nil
  end
end
```

#### Consumer Version Selectors (tags — legacy, still supported)

```javascript
consumerVersionSelectors: [
  { tag: "main", latest: true },
  { tag: process.env.GIT_BRANCH, latest: true }, // matching feature branch
  { tag: "test", latest: true },
  { tag: "production", latest: true },
];
```

### Pending pacts

Enable `enablePending: true` (or `enable_pending true` in Ruby). This ensures the build only fails when a breaking change causes a **previously supported** pact to fail. New consumer interactions (that have never been verified) will not break the provider build.

**Prerequisites**: consumer must set `mainBranch` property and publish with a branch name; provider must set branch/version when verifying.

### Work In Progress (WIP) pacts

Enable with `includeWipPactsSince: "YYYY-MM-DD"`. Automatically includes new pacts (that haven't been verified on the provider's main branch) without requiring manual config changes. Best enabled only on the provider's main branch.

### Verification triggered by a pact change (webhook-driven)

When triggered by a webhook (pact URL passed via env var):

```javascript
// Only use pactUrls — don't set pactBrokerUrl or consumerVersionSelectors
const verificationOptions = {
  pactUrls: [process.env.PACT_URL],
  publishVerificationResult: process.env.CI === "true",
  providerVersion: process.env.GIT_COMMIT,
  providerVersionBranch: process.env.GIT_BRANCH,
};
```

---

## Handling Authentication

> Source: https://docs.pact.io/provider/handling_auth.md

**Option 1: Test auth code outside of Pact**
Authentication protocols use stable patterns. Consider whether auth needs to be in the contract, or whether it can be covered by canary tests or lightweight integration tests.

**Option 2: Stub auth services with hardcoded credentials**
Stub auth/authz services to accept pre-agreed hardcoded credentials. Most practical for OAuth, JWT, and similar.

**Option 3: Set up users with matching credentials using provider states**
Pre-agree on credentials and set up the matching user data in a provider state. Works when user data is internal to the service.

**Option 4: Modify the request using a request filter**
Most Pact implementations allow modifying the request before it is replayed (e.g. pact-jvm request filter, Ruby Rack middleware). Use this to inject real credentials.

**Option 5: Use a long-lived token**
Not ideal, but pragmatic if other options are impractical.

---

## Fixing Failing Verification Tests

> Source: https://docs.pact.io/provider/how_to_fix_failing_verification_tests.md

For Ruby, JS, Python, Go, Swift:

1. Identify the failing branch from CI. Check it out locally.
2. Run pact tests locally with read-only credentials.
3. Isolate the first failing interaction using `PACT_BROKER_INTERACTION_ID`:
   ```bash
   export PACT_BROKER_INTERACTION_ID="3c82fcae84594130d4d118d0d790440f41e630db"
   # Then run the verification task — only the failing interaction will run
   ```
4. Locate the provider state setup code by searching for the state string (e.g. "Given a webhook has been triggered").
5. Determine whether it's a code fix or a data fix.
6. Fix and repeat with the next error.
7. Commit and push.

**Common causes of failures**:

- Provider state not set up correctly (missing or wrong test data)
- Wrong field type being returned (string vs number)
- Missing required fields in the response
- Request format changed on the consumer side
- Auth headers not being injected correctly

Use the MCP tool to diagnose:

```
contract-testing_review_pact_tests
  pact_tests: "<content of failing pact file>"
  error_messages: "<output from the failing test run>"
```

---

## Using Provider States Effectively

> Source: https://docs.pact.io/provider/using_provider_states_effectively.md

### The false positive problem

Consumer declares: `GET /search-alligators?name=Mary` — expects one alligator named Mary.

But the correct parameter is `?firstname=Mary`. The provider ignores the wrong param and returns the one alligator (set up in the provider state). Test passes. In production, the wrong param is ignored and ALL alligators are returned.

### Solution 1: Echo the query params in the response

Provider returns which params it actually used:

```json
{ "query": { "firstname": ["Mary"] }, "alligators": [...] }
```

The consumer test would fail if it expected `{"query": {"name": ...}}`, revealing the bug.

### Solution 2: Use multiple data points in the provider state

Provider state: "an alligator named Mary exists AND an alligator named John exists"

Consumer expects: exactly one alligator named Mary.

If the wrong param is used, both alligators are returned, the count assertion fails, and the bug is caught.

### Naming provider states

- Make state names descriptive and reusable: `"user with ID 123 exists"`, `"no users exist"`, `"user 123 has an expired subscription"`
- Before writing new consumer tests, call `contract-testing_get_provider_states` to reuse existing state names
- Keep states minimal — set up exactly what is needed for the interaction, nothing more


path: references/pact-broker-setup.md
content: # Pact Broker Setup, CLI & Troubleshooting

> Sources: https://docs.pact.io/pact_broker/set_up_checklist.md, https://docs.pact.io/pact_broker/client_cli/readme.md, https://docs.pact.io/pact_broker/webhooks/debugging_webhooks.md, https://docs.pact.io/pact_broker/webhooks/dynamic_variable_sub.md, https://docs.pact.io/pact_broker/advanced_topics/troubleshooting.md

---

## Setup Checklist

### Basic Setup (get pacts generated and verified automatically)

**Anyone:**

- Set up a [Pact Broker](https://github.com/pact-foundation/pact_broker) instance, or create a [PactFlow](https://pactflow.io/pricing/) account

**Consumer team:**

- Write consumer tests using the Pact mock provider (generates pact files)
- Publish pacts to the broker in CI with a branch tag (`main`, `feat-foo`)
- Use the git SHA as the version number

**Provider team:**

- Add a pact verification test to CI
- Configure verification to verify pacts from the consumer's `main` branch
- Only publish verification results from CI (not local)
- Create a separate CI job that just verifies a specific pact URL (for webhook-triggered builds)
- Create a webhook that triggers that job when a pact changes (pass the pact URL via webhook template)

### Advanced Setup (safe independent deployments)

**Consumer team:**

- Record deployments: tag the version in the broker when deploying to each environment
- Add `can-i-deploy` check before deploying

**Provider team:**

- Also verify pacts from the production environment (`{ deployed: true, environment: "production" }`)
- Record deployments: tag the provider version when deploying to each environment
- Add `can-i-deploy` check before deploying

### Super Advanced (when can-i-deploy fails due to missing production verification)

If `can-i-deploy` fails because you've never verified the production provider version against the new consumer version:

1. Deploy the latest provider version to production (recommended)
2. OR check out the production provider version and run verification against the failing consumer pact

Find the production provider version:

```bash
pact-broker describe-version --pacticipant MyProvider --latest production
```

---

## Pact Broker CLI Reference

### Installation

```bash
# Docker (no Ruby required)
docker pull pactfoundation/pact-cli:latest

# Standalone binary (no Ruby required)
# Download from: https://github.com/pact-foundation/pact-ruby-standalone/releases

# Ruby gem
gem install pact_broker-client
```

### Authentication

Set one of these (or pass as flags):

```bash
# Bearer token (PactFlow)
export PACT_BROKER_BASE_URL="https://yourorg.pactflow.io"
export PACT_BROKER_TOKEN="your-api-token"

# Basic auth (open-source Pact Broker)
export PACT_BROKER_BASE_URL="https://your-broker.example.com"
export PACT_BROKER_USERNAME="admin"
export PACT_BROKER_PASSWORD="password"
```

---

### Publishing Pacts

```bash
pact-broker publish ./pacts \
  --consumer-app-version $GIT_COMMIT \
  --branch $GIT_BRANCH \
  --broker-base-url $PACT_BROKER_BASE_URL
```

Options:

- `-a` / `--consumer-app-version` — version number (use git SHA)
- `-h` / `--branch` — git branch name
- `-t` / `--tag` — legacy tag (prefer branch)
- `--merge` — merge with existing pact content (for parallel consumer builds)
- `--build-url` — CI build URL for traceability

---

### Environments

```bash
# List all environments
pact-broker list-environments

# Create an environment
pact-broker create-environment \
  --name production \
  --display-name "Production" \
  --production   # marks it as a production environment

# Create a staging environment
pact-broker create-environment --name staging --display-name "Staging"

# Describe an environment
pact-broker describe-environment --uuid <uuid>

# Delete an environment
pact-broker delete-environment --uuid <uuid>
```

---

### Recording Deployments and Releases

```bash
# Record a deployment (replaces previous deployed version)
pact-broker record-deployment \
  --pacticipant MyApp \
  --version $GIT_COMMIT \
  --environment production

# Record undeployment (only needed when permanently removing from environment)
pact-broker record-undeployment \
  --pacticipant MyApp \
  --environment production

# Record a release (mobile/library — multiple versions coexist)
pact-broker record-release \
  --pacticipant MyMobileApp \
  --version $VERSION \
  --environment production

# End support for a released version
pact-broker record-support-ended \
  --pacticipant MyMobileApp \
  --version $OLD_VERSION \
  --environment production
```

---

### can-i-deploy

```bash
# Basic check
pact-broker can-i-deploy \
  --pacticipant MyApp \
  --version $GIT_COMMIT \
  --to-environment production

# With polling (wait for verification results)
pact-broker can-i-deploy \
  --pacticipant MyApp \
  --version $GIT_COMMIT \
  --to-environment production \
  --retry-while-unknown 10 \
  --retry-interval 10

# Dry run (always exits 0 — use to validate setup)
pact-broker can-i-deploy \
  --pacticipant MyApp \
  --version $GIT_COMMIT \
  --to-environment production \
  --dry-run

# Check by branch (not version)
pact-broker can-i-deploy \
  --pacticipant MyApp \
  --branch main \
  --to-environment staging

# Check using the latest of the configured main branch
pact-broker can-i-deploy \
  --pacticipant MyApp \
  --main-branch \
  --to-environment staging
```

**can-i-merge**: Checks if the specified version is compatible with the main branch of each integrated application (useful before merging a PR):

```bash
pact-broker can-i-merge --pacticipant MyApp --version $GIT_COMMIT
```

---

### Pacticipants

```bash
# Create or update a pacticipant
pact-broker create-or-update-pacticipant \
  --name MyService \
  --display-name "My Service" \
  --main-branch main \
  --repository-url https://github.com/myorg/my-service

# List all pacticipants
pact-broker list-pacticipants

# Describe a pacticipant
pact-broker describe-pacticipant --name MyService
```

---

### Webhooks

```bash
# Create a webhook (tip: build curl command first, then adapt)
pact-broker create-webhook \
  "https://ci.example.com/api/builds" \
  --request POST \
  --header "Content-Type: application/json" \
  --data '{"pact_url": "${pactbroker.pactUrl}", "branch": "${pactbroker.consumerVersionBranch}"}' \
  --consumer FrontendApp \
  --provider UserService \
  --contract-requiring-verification-published \
  --description "Trigger UserService build when FrontendApp pact changes"

# Test a webhook
pact-broker test-webhook --uuid <webhook-uuid>

# Create-or-update webhook (idempotent, good for infrastructure-as-code)
pact-broker generate-uuid  # generate a UUID first
pact-broker create-or-update-webhook \
  "https://ci.example.com/api/builds" \
  --uuid <generated-uuid> \
  --request POST \
  ...
```

**Webhook event flags**:

- `--contract-content-changed` — pact published with changed content
- `--contract-published` — pact published for the first time
- `--contract-requiring-verification-published` — pact needs verification (recommended)
- `--provider-verification-published` — verification result published
- `--provider-verification-failed` — verification failed
- `--provider-verification-succeeded` — verification succeeded

---

### Branches and Tags

```bash
# Delete a stale feature branch (does not delete pacts, just the branch record)
pact-broker delete-branch --branch feat/old-feature --pacticipant MyConsumer

# Create a version tag (legacy)
pact-broker create-version-tag \
  --pacticipant MyConsumer \
  --version $GIT_COMMIT \
  --tag production
```

---

### Versions

```bash
# Describe the latest version of a pacticipant
pact-broker describe-version --pacticipant MyService

# Describe a specific version
pact-broker describe-version --pacticipant MyService --version $GIT_COMMIT

# Describe latest version with a specific tag
pact-broker describe-version --pacticipant MyService --latest production
```

---

### PactFlow-only: Publishing Provider Contracts (BDCT)

```bash
pactflow publish-provider-contract openapi.yaml \
  --provider MyProvider \
  --provider-app-version $GIT_COMMIT \
  --branch $GIT_BRANCH \
  --specification oas \
  --content-type application/yaml \
  --verification-success \
  --verifier dredd \
  --verifier-version 13.0.0
```

See `references/bdct.md` for full BDCT workflow.

---

## Webhook Debugging

### Why didn't my webhook fire?

Search application logs for the consumer version number. Look for lines like:

- `"Pact content has not changed since previous version, not triggering webhooks"` — no change in pact content, so webhook didn't fire (by design)
- `"No enabled webhooks found for consumer X and provider Y"` — no webhook configured for this pair
- `"Webhook triggered for the following reasons: ..."` — webhook did fire, check the scheduling logs
- `"Scheduling job for webhook with uuid ..."` — webhook is queued

Set `PACT_BROKER_LOG_LEVEL=DEBUG` and `PACT_BROKER_SQL_LOG_LEVEL=none` to see more detail.

### Testing a webhook manually

**PactFlow**: Settings → Webhooks → select webhook → Edit → scroll down → TEST button

**Open-source Pact Broker**:

1. Go to `/webhooks` in the API Browser
2. Navigate to the webhook via `pb:webhooks` relation
3. Click `NON-GET` on the `pb:execute` relation → Make Request
4. Execution logs are returned on the next page

### Viewing past webhook execution logs

1. On the Pact Broker index page, the second-to-last column shows webhook status per pact
2. Click the link to get the webhook status resource
3. If there's an error: look for `pb:error-logs` relation in the Links section

**Note**: Execution logs are hidden by default for security. Configure `PACT_BROKER_WEBHOOK_HOST_WHITELIST` or `webhook_host_whitelist` to enable viewing logs from specific hosts.

### Creating webhooks — best practice

1. Get the API call working first with `curl` or Postman
2. Use the curl command to create the webhook with `pact-broker create-webhook`
3. For credentials: store in PactFlow Secrets (Settings → Secrets) and reference via `${pactbroker.secretName}`

---

## Webhook Dynamic Variable Substitution

The following `${pactbroker.*}` variables can be used in webhook request paths, parameters, and bodies:

| Variable                                    | Value                                               |
| ------------------------------------------- | --------------------------------------------------- |
| `${pactbroker.consumerName}`                | Consumer name                                       |
| `${pactbroker.providerName}`                | Provider name                                       |
| `${pactbroker.consumerVersionNumber}`       | Consumer version number                             |
| `${pactbroker.providerVersionNumber}`       | Provider version number (verification result)       |
| `${pactbroker.consumerVersionTags}`         | Consumer version tags, comma-separated              |
| `${pactbroker.providerVersionTags}`         | Provider version tags, comma-separated              |
| `${pactbroker.consumerVersionBranch}`       | Consumer version branch name                        |
| `${pactbroker.providerVersionBranch}`       | Provider version branch name                        |
| `${pactbroker.consumerLabels}`              | Consumer labels, comma-separated                    |
| `${pactbroker.providerLabels}`              | Provider labels, comma-separated                    |
| `${pactbroker.pactUrl}`                     | Permalink URL to the published pact                 |
| `${pactbroker.verificationResultUrl}`       | URL to the verification result                      |
| `${pactbroker.githubVerificationStatus}`    | Verification status for GitHub commit status API    |
| `${pactbroker.bitbucketVerificationStatus}` | Verification status for Bitbucket commit status API |

### Example webhook body

```json
{
  "events": [{ "name": "contract_requiring_verification_published" }],
  "request": {
    "method": "POST",
    "url": "https://api.github.com/repos/myorg/provider/actions/workflows/pact.yml/dispatches",
    "headers": {
      "Authorization": "Bearer ${pactbroker.githubToken}",
      "Content-Type": "application/json"
    },
    "body": {
      "ref": "main",
      "inputs": {
        "pact_url": "${pactbroker.pactUrl}",
        "consumer_version_branch": "${pactbroker.consumerVersionBranch}"
      }
    }
  }
}
```

For pre-built webhook templates (GitHub Actions, CircleCI, GitLab, Jenkins, Azure DevOps), see: https://docs.pact.io/pact_broker/webhooks/template_library

---

## Troubleshooting

### 409 Conflict When Publishing a Pact

The Pact Broker prevents creating pacticipants with similar names (e.g. `FooBar` vs `foo-bar` vs `foo bar`).

**Fix**: Manually create the pacticipant with the exact desired name:

```bash
pact-broker create-or-update-pacticipant --name "ExactServiceName"
```

Or via the MCP tool:

```
contract-testing_create_pacticipant
  name: "ExactServiceName"
```

To disable the duplicate checking: set `PACT_BROKER_CHECK_FOR_POTENTIAL_DUPLICATE_PACTICIPANT_NAMES=false` (not recommended).

### `schema_migrations does not exist` on Startup

This is **not an error**. The Pact Broker checks if tables exist by attempting to query them — if they don't exist yet, it logs an error but then creates them. This is expected behavior on first startup and can be safely ignored.

### `can-i-deploy` returns error for unknown application

If your org has a universal CI pipeline and some apps don't use Pact, wrap `can-i-deploy`:

```bash
pact-broker can-i-deploy \
  --pacticipant $APP \
  --version $VERSION \
  --to-environment production \
  --dry-run  # always exits 0 while setting up
```

Or use logic to skip when the app isn't in the broker:

```bash
pact-broker can-i-deploy ... || echo "Not in Pact Broker — skipping"
```

### Pact verification not publishing results

Common causes:

1. `publishVerificationResult` / `publish_verification_results` is `false` or only set in CI
2. Provider version or branch not set (required for result association)
3. Pact Broker URL/token is incorrect

Verify the provider version is set:

```bash
# Should be your git SHA
echo $GIT_COMMIT
```


path: references/tools.md
content: # PactFlow MCP Tools Reference

All tools use the `contract-testing_` prefix. Tools marked **Cloud only** require PactFlow Cloud (`PACT_BROKER_TOKEN`). All others work with both PactFlow and open-source Pact Broker.

## Table of Contents

1. [AI Tools](#ai-tools) (Cloud only)
2. [Deployment Safety](#deployment-safety)
3. [Contracts](#contracts)
4. [Pacticipants](#pacticipants)
5. [Branches & Versions](#branches--versions)
6. [Environments & Deployments](#environments--deployments)
7. [Bi-Directional Contract Testing](#bi-directional-contract-testing) (Cloud only)
8. [Integrations & Network](#integrations--network)
9. [Labels](#labels)
10. [Metrics](#metrics)
11. [Webhooks](#webhooks)
12. [Secrets](#secrets)

---

## AI Tools

**`contract-testing_generate_pact_tests`** ☁️ Cloud only
Generate Pact tests using PactFlow AI.

- `language` — target language (inferred if omitted and code files provided)
- `request_response.request` / `.response` — HTTP interaction to generate from
- `code` — array of code files (client code, models, utilities)
- `openapi.document` — OpenAPI spec content; `openapi.matcher` — filter to specific endpoint/method/status
- `openapi.remoteDocument` — URL + auth for a remote OpenAPI spec
- `additional_instructions` — extra constraints or framework preferences
- `test_template` — existing test file to use as a structural template

**`contract-testing_review_pact_tests`** ☁️ Cloud only
Review existing Pact tests against best practices.

- `pact_tests` — the test files to review (required)
- `code` — supporting code files for context
- `openapi.document` / `.matcher` / `.remoteDocument` — API spec context
- `user_instructions` — areas to focus on
- `error_messages` — output from failed test runs to help diagnose issues

**`contract-testing_check_pactflow_ai_entitlements`** ☁️ Cloud only
Diagnose 401 errors or credit issues when using AI tools. No parameters.

---

## Deployment Safety

**`contract-testing_can_i_deploy`**
Check whether a version is safe to deploy to an environment.

- `pacticipant` — service name (required)
- `version` — version number (required)
- `environment` — target environment name, e.g. `production` (required)

**`contract-testing_matrix`**
Query the contract verification matrix.

- `q` — array of 1–2 pacticipant selectors, each with: `pacticipant`, and one of `version`, `branch`, `environment`, `latest`, `tag`, `mainBranch`
- `latestby` — `cvp` (latest per consumer version + provider) or `cvpv` (latest per consumer version + provider version)
- `limit` — max rows (1–1000, default 100)

---

## Contracts

**`contract-testing_publish_consumer_contracts`**
Publish consumer pact files to the broker.

- `pacticipantName`, `pacticipantVersionNumber` — consumer name and version (required)
- `contracts` — array of `{ consumerName, providerName, content (base64), contentType: "application/json", specification: "pact" }`
- `branch` — consumer branch name
- `tags` — version tags array
- `buildUrl` — CI build URL

**`contract-testing_publish_provider_contract`** ☁️ Cloud only (BDCT)
Publish an OpenAPI spec + self-verification results.

- `providerName`, `pacticipantVersionNumber` — required
- `contract.content` — base64-encoded OpenAPI spec
- `contract.contentType` — `application/yaml`, `application/json`, or `application/yml`
- `contract.specification` — must be `oas`
- `contract.selfVerificationResults.success` — boolean (required)
- `contract.selfVerificationResults.verifier` — tool name, e.g. `dredd`, `schemathesis`
- `branch`, `tags`, `buildUrl` — optional metadata

**`contract-testing_get_pacts_for_verification`**
Fetch pacts a provider should verify in its CI run.

- `providerName` — required
- `consumerVersionSelectors` — array of selectors: `{ mainBranch: true }`, `{ deployedOrReleased: true }`, `{ branch: "feat/x" }`, `{ environment: "staging" }`
- `includePendingStatus` — boolean; pending pacts won't fail the provider build
- `includeWipPactsSince` — ISO 8601 date; include new consumer interactions for early feedback
- `providerVersionBranch`, `providerVersionTags` — metadata about the provider being verified

**`contract-testing_get_provider_states`**
List all provider states defined for a provider.

- `provider` — provider name (required)

---

## Pacticipants

**`contract-testing_list_pacticipants`**
List all registered services. Optional: `pageNumber`, `pageSize`.

**`contract-testing_get_pacticipant`**
Get metadata for a service. `pacticipantName` required.

**`contract-testing_create_pacticipant`**
Register a new service. `name` required. Optional: `displayName`, `mainBranch`, `repositoryUrl`, `repositoryName`, `repositoryNamespace`.

**`contract-testing_update_pacticipant`**
Fully replace pacticipant metadata (clears unset fields). Same params as create plus `pacticipantName`.

**`contract-testing_patch_pacticipant`**
Partially update pacticipant metadata (only provided fields change). Prefer over update for targeted changes.

**`contract-testing_delete_pacticipant`**
Delete a service and all its data. `pacticipantName` required. Irreversible.

---

## Branches & Versions

**`contract-testing_list_branches`**
List branches for a pacticipant. `pacticipantName` required. Optional: `q` (name filter), `pageNumber`, `pageSize`.

**`contract-testing_get_branch`**
Get branch metadata. `pacticipantName`, `branchName` required.

**`contract-testing_delete_branch`**
Delete a branch and its version associations. `pacticipantName`, `branchName` required.

**`contract-testing_get_branch_versions`**
List versions published from a branch. `pacticipantName`, `branchName` required. Optional: `pageNumber`, `pageSize`.

**`contract-testing_list_pacticipant_versions`**
List all versions for a service. `pacticipantName` required. Optional: `pageNumber`, `pageSize`.

**`contract-testing_get_pacticipant_version`**
Get metadata for a specific version. `pacticipantName`, `versionNumber` required.

**`contract-testing_get_latest_pacticipant_version`**
Get the most recent version, optionally filtered by tag. `pacticipantName` required. Optional: `tag`.

**`contract-testing_update_pacticipant_version`**
Update version metadata. `pacticipantName`, `versionNumber` required. Optional: `buildUrl`.

**`contract-testing_get_deployed_versions_for_version`**
Check deployment records for a version in an environment. `pacticipantName`, `versionNumber`, `environmentId` required.

**`contract-testing_get_released_versions_for_version`**
Check release records for a version in an environment (mobile/library workflows). Same params.

---

## Environments & Deployments

**`contract-testing_list_environments`**
List all environments and their UUIDs. No parameters.

**`contract-testing_get_environment`**
Get environment details. `environmentId` (UUID) required.

**`contract-testing_create_environment`**
Create an environment. `name` required. Optional: `displayName`, `production` (boolean).

**`contract-testing_update_environment`**
Replace environment config. `environmentId`, `name` required. Optional: `displayName`, `production`.

**`contract-testing_delete_environment`**
Delete an environment. `environmentId` required. Also removes all deployment/release records.

**`contract-testing_record_deployment`**
Record a deployment. `pacticipantName`, `versionNumber`, `environmentId` required. Optional: `applicationInstance` (e.g. `blue`, `green`).

**`contract-testing_get_currently_deployed_versions`**
List currently deployed versions in an environment. `environmentId` required.

**`contract-testing_record_release`**
Record a release (mobile/library). `pacticipantName`, `versionNumber`, `environmentId` required.

**`contract-testing_get_currently_supported_versions`**
List currently supported (released) versions in an environment. `environmentId` required.

---

## Bi-Directional Contract Testing

All BDCT tools are **Cloud only**. See `bdct.md` for patterns and investigation workflows.

**`contract-testing_get_bdct_provider_contract`**
Fetch the published OpenAPI spec for a provider version.
`providerName`, `providerVersionNumber` required.

**`contract-testing_get_bdct_provider_contract_verification_results`**
Fetch self-verification results (Dredd, Schemathesis, etc.) for a provider version.
`providerName`, `providerVersionNumber` required.

**`contract-testing_get_bdct_consumer_contracts`**
Fetch all consumer pacts compared against a provider version.
`providerName`, `providerVersionNumber` required.

**`contract-testing_get_bdct_consumer_contract_verification_results`**
Fetch results of comparing all consumer pacts against the provider's OpenAPI spec.
`providerName`, `providerVersionNumber` required.

**`contract-testing_get_bdct_cross_contract_verification_results`**
Fetch the combined cross-contract comparison result — the key result for can-i-deploy.
`providerName`, `providerVersionNumber` required.

**By consumer version** (add `consumerName`, `consumerVersionNumber` to any of the above):

- `contract-testing_get_bdct_consumer_contract_by_consumer_version`
- `contract-testing_get_bdct_provider_contract_by_consumer_version`
- `contract-testing_get_bdct_provider_contract_verification_results_by_consumer_version`
- `contract-testing_get_bdct_consumer_contract_verification_results_by_consumer_version`
- `contract-testing_get_bdct_cross_contract_verification_results_by_consumer_version`

---

## Integrations & Network

**`contract-testing_list_integrations`**
List all consumer-provider pairings in the workspace. No parameters.

**`contract-testing_get_pacticipant_network`**
Get the integration graph for a service — all consumers that depend on it, and all providers it depends on. `pacticipantName` required. Useful for blast radius analysis.

---

## Labels

**`contract-testing_list_labels`**
List all labels used across the workspace. Optional: `pageNumber`, `pageSize`.

**`contract-testing_get_pacticipant_label`**
Check if a label is applied to a pacticipant. `pacticipantName`, `label` required. Returns 404 if not applied.

**`contract-testing_list_pacticipants_by_label`**
List all services with a given label. `label` required.

**`contract-testing_add_label`**
Apply a label to a pacticipant. `pacticipantName`, `label` required.

**`contract-testing_remove_label`**
Remove a label from a pacticipant. `pacticipantName`, `label` required.

---

## Metrics

**`contract-testing_get_metrics`**
Workspace-wide usage statistics. No parameters.

**`contract-testing_get_team_metrics`** ☁️ Cloud only
Per-team usage statistics. No parameters.

---

## Webhooks

**`contract-testing_list_webhooks`** / **`contract-testing_get_webhook`**
List or get webhook configurations. `uuid` required for get.

**`contract-testing_create_webhook`** / **`contract-testing_update_webhook`**
Create or update a webhook. Key fields: `description`, `events` (array), `request` (method, url, headers, body), optionally scoped to `consumer`, `provider`.

**`contract-testing_delete_webhook`**
Delete a webhook. `uuid` required.

**`contract-testing_execute_webhook`**
Trigger a webhook manually for testing. `uuid` required.

---

## Secrets

**`contract-testing_list_secrets`** / **`contract-testing_get_secret`**
List or get secrets (used in webhook configurations). `uuid` required for get.

**`contract-testing_create_secret`** / **`contract-testing_update_secret`**
Create or update a secret. `name`, `value` required.

**`contract-testing_delete_secret`**
Delete a secret. `uuid` required.


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)