Four responses, four different observations
An API consumer reads a plan's price and billing period. The amount stays 29, but the period can be a value, explicitly null, absent, or an empty string. A comparison that collapses all four states can lose the reason a consumer needs review.
These four authored JSON objects describe a fictional Team plan. They are downloadable fixtures, not responses captured from a production endpoint.
{"id":"team","price":29,"billingPeriod":"month"}
{"id":"team","price":29,"billingPeriod":null}
{"id":"team","price":29}
{"id":"team","price":29,"billingPeriod":""}
What the recorded check establishes
Scroll horizontally to see all columns.
| Fixture | Property exists | Observed state | Passes this demo's contract |
|---|---|---|---|
value | Yes | String month | Yes |
null | Yes | Explicit null | No |
missing | No | Absent property | No |
empty-string | Yes | Empty string | No |
The demonstration contract requires billingPeriod to exist and contain either month or year. That is an explicit rule we authored for this example. It is not a universal rule for APIs. An API that documents null as an allowed state should be evaluated against that different contract.
The JSON Schema null reference distinguishes a null value from an absent property. Its object reference explains required properties. Defining a property under properties does not by itself require the property to be present.
Keep presence and value separate
The public harness classifies the response before applying the example contract:
const present = Object.hasOwn(response, "billingPeriod");
const state = !present
? "missing"
: response.billingPeriod === null
? "null"
: response.billingPeriod === ""
? "empty-string"
: "value";
const passesDemoContract = present
&& ["month", "year"].includes(response.billingPeriod);
The value label here means that a non-null, non-empty value is present. It does not validate its type or meaning on its own. The contract check is a separate step. For example, week would be present and still fail this demonstration's allowed-value rule.
Avoid assuming that a missing period means monthly billing just because the prior response contained month. If the consumer intentionally uses a default, record that default and the condition that activates it. Otherwise an unavailable term can become a confidently displayed price with the wrong meaning.
Review the consumer consequence
Ask what the field controls. A billing period used in a label, renewal calculation or comparison needs a different response from an optional decorative subtitle. Keep the exact response and the consumer contract beside the detected difference.
For this example, the proposed review action is to withhold the derived monthly-price interpretation until the period is known. That is an illustrative handling decision, not an automatic behavior claimed for every OnChange integration.
A successful HTTP status would not settle this question. Transport success, parse success, contract validity and business interpretation are distinct checks. This local experiment starts with parsed JSON and does not exercise HTTP, authentication, retries, latency or a remote server.
Download and repeat
The four API fixtures preserve the original objects. The api_cases array in the results record contains the measured presence, classified state and contract result for each object.
Use the reproduction kit to run the original harness. It asserts that the states stay distinct and that only value passes this example's contract. This is a small deterministic check, not a general JSON Schema validator.
For broader response comparison, read array order, missing fields and API risk. When the data describes an offer, also preserve price and billing terms together.