API Contract Monitoring: How to Catch Breaking Changes Before They Hit Production
You pushed a clean deploy. Tests passed. The pipeline went green. Then, two hours later, your frontend team is filing a P1 because a field your mobile client depends on silently changed type in a third-party response. Nobody announced it. The provider's status page still shows all-green.
This is the scenario that API contract monitoring is designed to prevent, and it catches things that traditional testing fundamentally cannot.
Why API Contracts Break in the First Place
An API contract is the implicit or explicit agreement between a producer and a consumer: this endpoint returns these fields, in these types, following this structure. When that agreement breaks, consumers fail.
What makes API contract drift difficult to defend against is that it often comes from outside your codebase entirely. Your own CI pipeline has zero visibility into what a third-party provider will return next Tuesday. Even internally, microservice teams ship independently. A downstream service can quietly remove a field, change a string to an integer, or add a new required parameter without filing a formal change notice.
The most common causes of production API breakage, based on patterns that appear repeatedly in incident post-mortems:
- Field removal - A provider removes a field they considered internal, not knowing you were using it.
- Type change -
transaction_idshifts from integer to prefixed string (txn_12345). Your code casts to int, gets 0, processes a $0 transaction. - Enum expansion - A
statusfield gains a new value your switch statement has no case for. - Nullable surprise - A previously reliable field starts returning
nullon edge cases the provider didn't document. - Key rename -
user_namebecomesusername. One underscore. Silent failure for any consumer doing exact key lookups. - Nested structure change - A flat object is wrapped in a parent key in a new API version that doesn't bump the version path.
Any one of these can drop production traffic to zero, and all of them can happen without a single line of your own code changing.
Why Testing Alone Does Not Catch This
Contract testing frameworks like Pact, OpenAPI spec validation, and Dredd-style integration tests are genuinely valuable tools. They solve a specific problem well: verifying that your own services honor agreed contracts at build time.
They have a structural blind spot, though.
Contract tests run against known baselines. They validate what the API should return according to a spec file or a recorded interaction you generated at some point in the past. The moment a third-party provider silently changes their live response, your spec file becomes stale documentation. Your CI pipeline still passes, because it's validating against the old recorded response, not the live one.
This is why reports consistently show that contract drift causes a disproportionate share of production failures despite CI pipelines being green. The test environment doesn't see what production sees.
| Approach | What It Validates | Catches Live Drift? |
|---|---|---|
| Unit tests | Internal logic | No |
| Contract tests (Pact, Dredd) | Spec-to-implementation match | Only against recorded baseline |
| OpenAPI spec linting | Spec structure and governance | No |
| Manual QA | Known flows | No |
| Runtime API monitoring | Live responses, continuously | Yes |
The missing layer is continuous runtime monitoring against real API responses. Spec-based testing and runtime monitoring are complementary, not competing. You need both.
What API Contract Monitoring Actually Does
Runtime API contract monitoring works differently from testing. Instead of validating against a static spec at build time, it polls live endpoints on a schedule, extracts specific fields from the response, and compares them to the previous run.
The moment a field changes, a type shifts, or a value crosses a threshold you defined, an alert fires - before any consumer in production has a chance to encounter the broken contract.
The core loop looks like this:
Poll endpoint → Extract fields → Diff against last run → Evaluate predicate → Fire alert if trueThe predicate step is what separates signal from noise. You don't want to fire an alert every time any byte in a response changes. You want to fire when the specific field you care about actually changes - or changes in a way that matters. A price field drifting by 0.01% is noise. A version field changing is signal.
What Changes Should Trigger an Alert
Not every response change represents a contract violation. A good monitoring strategy alerts on changes that indicate a broken contract, not changes that are expected variation.
Alert immediately:
- Required field disappears from response
- Field type changes (string to number, object to array)
- New
requiredfield appears that wasn't there before - Enum value changes or a known value is removed
- API version field bumps without a coordinated deploy on your side
- Error rate fields or status fields change unexpectedly
Monitor but don't page:
- Optional metadata fields changing
- Timestamp field values (expected)
- Pagination cursor changes
Ignore entirely:
- CDN cache headers
- Request ID values
- Dynamic timestamps within expected range
A field-level predicate system lets you encode this logic directly into the monitor, so you're never woken up for changes that don't matter.
Real Engineering Workflow
Here is a practical workflow for adding API contract monitoring to an existing service.
Step 1: Identify your critical external dependencies. List every third-party or cross-team API your service calls where a silent change would cause a production incident. Focus on data shape, not just availability. You probably already have availability monitoring. Contract drift is the gap.
Step 2: Identify the specific fields that matter. For each dependency, identify which fields your code actually reads. A payment API might return fifty fields, but your integration only reads status, amount, and transaction_id. Those three are your contract surface.
Step 3: Create monitors for each field. Set up a polling monitor against each endpoint. Extract only the fields you care about. Use JSONPath for JSON APIs - it's precise and easy to maintain.
Step 4: Define predicates based on what would actually break your code. For a status field, you might want to know any time the value changes. For a version field, you want to know any time it bumps. For a numeric field, you might want to know only if it changes by more than a threshold.
Step 5: Route alerts to the right channel. A breaking field change should land in your incident channel immediately. A version bump might just need a Slack notification to the relevant team. Route by severity.
Step 6: Establish baselines before they matter. Start monitoring before an incident. The first run establishes a baseline. Subsequent runs compare against it. If you set up monitors after something breaks, you've already missed the value.
Monitoring Third-Party JSON APIs with Verid
Verid is built specifically for this kind of continuous, field-level monitoring of any URL that returns structured data, including JSON REST APIs.
The core idea maps directly to the workflow above: you point Verid at an endpoint, tell it which fields to extract using JSONPath, define a predicate that captures what "this changed in a way I care about" means, and configure where to send the alert.
Here is a monitor that watches a dependency's version endpoint and fires any time the version field changes:
curl -X POST https://api.verid.dev/v1/monitors \
-H "Authorization: Bearer vrd_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Payments API - Version Drift",
"url": "https://api.payments-provider.com/version",
"schedule_interval_seconds": 300,
"extract_config": {
"method": "json_path",
"fields": {
"version": "$.api_version",
"deprecated": "$.deprecated"
}
},
"diff_predicate": {
"type": "composite",
"operator": "OR",
"conditions": [
{ "type": "field_changes", "field": "version" },
{ "type": "field_equals", "field": "deprecated", "value": true }
]
},
"deliveries": [
{ "type": "webhook", "url": "https://your-app.com/hooks/api-drift" },
{ "type": "slack" }
]
}'This monitor polls every 5 minutes. It fires if api_version changes OR if deprecated becomes true. Everything else - headers, timestamps, other fields - is ignored. No noise.
When the predicate fires, the webhook payload includes the before and after values for every changed field:
{
"monitor": "Payments API - Version Drift",
"fired": "field_changes",
"diff": {
"fields_changed": ["version"],
"before": { "version": "2024-01-01", "deprecated": false },
"after": { "version": "2025-03-15", "deprecated": false }
},
"at": "2026-06-15T09:31:00Z"
}That payload gives you a clear before/after diff you can act on immediately, log to your incident management system, or trigger a runbook against. Every webhook is HMAC-signed with your monitor's secret, so your endpoint can verify it came from Verid before processing it.
Here is the Node.js SDK equivalent, which is useful when you want to provision monitors programmatically alongside new service deployments:
import { VeridClient } from '@verid.dev/sdk';
const client = new VeridClient({ apiKey: process.env.VERID_API_KEY! });
await client.monitors.create({
name: 'Payments API - Version Drift',
url: 'https://api.payments-provider.com/version',
schedule_interval_seconds: 300,
extract_config: {
method: 'json_path',
fields: {
version: '$.api_version',
deprecated: '$.deprecated',
},
},
diff_predicate: {
type: 'composite',
operator: 'OR',
conditions: [
{ type: 'field_changes', field: 'version' },
{ type: 'field_equals', field: 'deprecated', value: true },
],
},
deliveries: [
{ type: 'webhook', url: 'https://your-app.com/hooks/api-drift' },
],
});The Node.js SDK is available on npm (@verid.dev/sdk) and mirrors the REST API closely, so switching between curl and code is straightforward.
For verifying incoming webhooks, the signature header is Verid-Signature and the signing method is HMAC-SHA256:
import { createHmac, timingSafeEqual } from 'crypto';
function verifySignature(header: string, rawBody: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const ts = parseInt(parts['t'] ?? '0', 10);
const sig = parts['v1'];
if (!ts || !sig) return false;
if (Math.abs(Date.now() / 1000 - ts) > 300) return false;
const expected = createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex');
return timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(sig, 'hex')
);
}Always verify the signature before processing the payload. Replay protection is built in via the 5-minute timestamp tolerance check.
Example Architecture: API Contract Monitoring in a Production Service
A typical setup for a payment-dependent SaaS service looks like this:
External Payment API
|
Verid monitor (polls every 5 min)
- Extracts: api_version, status, deprecated
- Predicate: version changes OR deprecated = true
|
Webhook → your /hooks endpoint
|
Route to PagerDuty / Slack / incident log
|
Runbook triggered or on-call engineer pagedThe critical insight is that this loop runs independently of your application code. You don't have to deploy anything to add a new monitor. You don't have to modify your service to start watching a new dependency. The monitoring layer is decoupled from the application layer.
This is valuable during incidents. When a third-party API changes and something breaks, Verid's field-level history shows you exactly which field changed and when. Root cause identification that might take 30 minutes of log analysis takes 30 seconds.
Breaking Change vs. Safe Change Reference
It helps to have a clear reference for what constitutes a breaking change versus an additive, backward-compatible change. This is what your monitoring predicates should be sensitive to.
| Change Type | Breaking? | Notes |
|---|---|---|
| Remove a required field | Yes | Consumers expecting it will fail |
| Rename an existing field | Yes | Same as remove from consumer perspective |
| Change field type | Yes | Type casting errors, silent data corruption |
| Remove an enum value | Yes | Unhandled case in consumer switch statements |
| Add a new required request field | Yes | Existing consumers don't send it |
| Add a new optional response field | No | Backward compatible |
| Add a new enum value | Potentially | Unhandled in strict consumers |
| Make optional field required | Yes | Existing consumers may not send it |
| Change HTTP status code | Yes | Consumer error handling breaks |
| Deprecate but still serve field | No | Backward compatible while deprecation period runs |
Best Practices
Monitor the fields you actually use, not the whole response. Trying to alert on any change in a large JSON response is a recipe for noise. List the fields your integration reads and monitor only those.
Set check frequency based on risk. A payment status endpoint that could silently start returning wrong data should poll every 5 minutes (Verid's Scale plan supports this). A dependency you check weekly can poll hourly.
Start monitoring before incidents, not after. The first run establishes a baseline. You need that baseline to exist before a change happens. Setting up monitoring in response to an outage helps for the next incident, not the current one.
Route alerts to the right place. A field type change should probably page on-call. A version bump with no consumer-visible impact might just need a Slack message. Use delivery routing to match severity.
Verify webhook signatures. Don't trust unsigned payloads from any monitoring system. Verid signs every delivery with HMAC-SHA256. Reject anything that doesn't verify.
Keep monitor names descriptive. When an alert fires at 3am, the monitor name is the first thing you read. "Payments API - Version Drift" is better than "Monitor 4".
Common Mistakes
Monitoring availability but not contract. An HTTP 200 from a dependency tells you the endpoint is reachable. It says nothing about whether the response shape is what your consumer expects. Availability monitoring and contract drift monitoring are separate concerns.
Only monitoring your own APIs. Internal API changes at least go through your own code review. Third-party APIs can change any time. The contracts most likely to drift without warning are the ones you don't control.
Waiting for users to report the issue. If the first signal that a third-party API changed comes from a customer support ticket, the detection loop is too slow. Proactive monitoring closes the gap from hours to minutes.
Setting up monitors after an incident. It's a natural reflex to add monitoring after something breaks. The value of contract monitoring is catching the next change before it becomes an incident. Start monitoring during normal operations.
Conclusion
API contract monitoring is the runtime layer that spec-based testing cannot cover. Tests validate against baselines you control. Runtime monitoring watches what actually flows over the wire, continuously.
The combination is more robust than either alone: spec validation and contract testing catch issues before merge, while continuous field-level monitoring catches what changes at the source after you've deployed.
If your service depends on third-party JSON APIs, or on internal services owned by other teams, consider which of those dependencies could silently drift and break your consumers. Those are the endpoints that need monitoring, not just testing.
Verid's JSON API field monitoring use case covers this pattern end to end, with JSONPath extraction, field-level diff history, and predicate-driven webhooks. The free plan includes 5 monitors and daily checks, with no credit card required. If you're evaluating whether this approach makes sense for your stack, the quickstart runs in under two minutes and produces a working monitor against a real endpoint.
Frequently Asked Questions
What is the difference between API contract testing and API contract monitoring?
Contract testing (Pact, Dredd, OpenAPI validation) validates that a service matches a spec at build time, typically using recorded interactions or static spec files. API contract monitoring polls live endpoints on a schedule and compares actual responses field-by-field to detect drift in production, including from third-party providers who don't participate in your contract testing setup. Both are useful; they catch different failure modes.
Can API contract monitoring catch breaking changes from third-party APIs I don't control?
Yes, and this is where it provides the most value. Spec-based contract testing requires cooperation from both the provider and the consumer. Runtime monitoring only needs HTTP access to the endpoint. You define what you expect, poll the live API on a schedule, and get alerted when the actual response stops matching your expectations, regardless of whether the provider announced the change.
How often should I poll an external API for contract drift?
It depends on how quickly a breaking change in that API would cause a production incident, and how quickly you want to detect it. For payment or authentication APIs where a silent change could immediately affect all users, polling every 5 minutes is reasonable. For less critical dependencies, every hour may be sufficient. Consider the recovery time you need as a factor: if it takes your team 30 minutes to respond and roll back, you want to detect drift before it affects 30 minutes of traffic.
What should a webhook payload include when a field-level change is detected?
At minimum, the payload should include: which monitor fired, which field changed, the before and after values, and a timestamp. Before/after values are essential for triage. A payload that just says "something changed" forces you to look up the previous state manually. Field-level diff data is what makes it actionable within seconds of receiving the alert.
About the author
Software Engineer & Technical Writer
A software engineer and technical writer, Hanzala works on Verid’s API surface and everything written about it. His posts cover website change detection, scraping trade-offs, and integrating monitoring alerts into an existing stack.
More from HANZALA SALEEM →Related posts
How to Monitor Job Listings for Keywords and Get Instant Alerts
Learn how to track new job postings automatically using Verid. Set keyword filters, configure smart alerts, and get notified the moment a match appears.
Read the post →monitoringAPI Monitoring vs Scraping: Why the Loop Is the Product
Discover why API monitoring outperforms scraping. Learn how Verid.dev simplifies change detection with continuous monitoring and webhooks.
Read the post →monitoringStop Monitoring Noise: How Predicate-Based Alerting Saves Your Sanity
Learn how predicate-based alerting filters out monitoring noise so you only get notified when a real condition you care about is actually true.
Read the post →monitoringXPath Tutorial: A Practical Guide for Web Scraping and Monitoring
Learn XPath syntax, axes, functions, and real scraping examples. Discover how to use XPath for website monitoring with Verid's change detection API.
Read the post →