Most API bugs that reach production are not outages. They are shape changes: a field renamed,
a number that became a string, an array that is now null when empty. A status code will not
catch any of those. A schema will.
Define what you expect
Open the Schema tab and describe the response in the schema language:
schema User {
id: string & format("uuid")
email: string & format("email")
plan: "free" | "pro"
created_at: string & format("date-time")
nickname?: string # optional — may be absent
bio: string? # nullable — must be present, may be null
teams: Team[]
}
Fields are required by default. The two modifiers are the ones that matter most in practice, and they are independent:
nickname?: string— the key may be missing from the object.bio: string?— the key must be there, but the value may be null.avatar?: string?— both are allowed.
"Sometimes missing" and "sometimes null" are different failure modes, and a consumer that handles one often crashes on the other. Being explicit about which you mean is most of the value of writing the schema at all.
Constrain the values, not just the types
A type alone catches a lot, but the interesting bugs live in the values. Constraints chain
with &:
type Username = string & minLength(3) & maxLength(32) & pattern("[a-z0-9_]+")
type Age = integer & min(0) & max(150)
type Tags = string[] & minItems(1) & maxItems(20) & uniqueItems
An integer & min(0) catches the negative quantity that a bare number would wave through.
A union like "free" | "pro" catches the day someone adds a third plan and forgets to tell
the client team.
Validate a real response
Send the request, then validate the response against the schema. You get a pass, or a list of exactly which fields diverged and how. That list is the useful part: it tells you whether the server changed or your expectation was wrong.
Validation is not limited to REST. The same schema file can describe GraphQL, WebSocket frames, SSE events and gRPC messages, so one contract covers every protocol your API speaks. Full syntax is in the schema language reference.
Three ways to get a schema
Write it by hand. Best when you own the API and know the contract.
Import an OpenAPI spec. If the API publishes OpenAPI 3.x, import it rather than retyping it — see importing an OpenAPI spec. The response schemas come across directly, which also surfaces a common surprise: the documented contract and the live response frequently disagree, and validating the real endpoint against its own spec is often the fastest way to prove it.
Generate from real traffic. Capture responses in the traffic monitor and derive a schema from what the endpoint actually returns. Generated schemas start permissive — the generator can only see the values it was given, so an ID that happened to be non-null in every sample looks non-nullable. Treat the generated file as a first draft and tighten it: narrow the unions, mark the genuinely optional fields, add the format constraints.
This third route is what makes validation possible for third-party APIs that never published a spec, which is the majority of them.
Run it in CI
A schema you check by hand catches drift the week you remember to look. To catch it the day
it happens, run the check in a pipeline: @schemaclient/cli validates responses against the
same schema files and exits non-zero on a mismatch, with a JUnit report for your CI to
render. See running API tests in CI.
Run against a live or staging environment rather than a mock where you can. A mock returns what you told it to return, which encodes the very assumption being tested.
Where this pays off
- Third-party APIs you do not control, which change without telling you.
- Webhook payloads — see testing webhooks locally. Payload shapes evolve without warning and the failure is usually a quiet error in a background job.
- Your own API, checked before release, so a serializer change does not ship a breaking rename to mobile clients that cannot be force-updated.
The broader case for this — and why a green test suite still misses it — is in when API tests pass but production breaks.
Schemas sync across your devices when you are signed in, alongside your collections.