# Compare the specification against your version

By the end, you have the specification of the current train, saved with a date, next to that of your environment, and you know whether a path has disappeared, a required field has been added, or a response carries a different status, before your integration notices.

> **Type:** guide
> **For:** Anyone who operates an integration and wants to check after every train whether it still fits.
> **Prerequisite:** `curl`, `jq`, and Bash (step 5 uses `<( … )`, which a POSIX `sh` does not know). No sign-in is needed, because the specification is public. For the second source, the base address of your environment in `SZENERA_API`.
> **Where:** Command line

The developer site serves the specification as of the current train at `/api/openapi.json`. Your environment responds at `/v1/openapi.json` with what actually runs there. Staging and production can differ, and only the comparison tells you which change you already have in front of you and which one is still coming.

## Steps

### Load both specifications

1. Save the specification of the site with a date:

   ```bash
   curl -sS https://developer.szenera.com/api/openapi.json -o "openapi-$(date +%F).json"
   ```

2. Place the specification of your environment next to it:

   ```bash
   curl -sS "$SZENERA_API/v1/openapi.json" -o openapi-umgebung.json
   ```

3. Note which version runs there:

   ```bash
   curl -sS "$SZENERA_API/health/version"
   ```

   ```json
   { "service": "api", "version": "0.4.14", "builtAt": "2026-09-07T10:12:00+02:00", "environment": "production" }
   ```

### Compare version and paths

4. Compare `info.version` of both files:

   ```bash
   jq -r '.info.version' openapi-umgebung.json "openapi-$(date +%F).json"
   ```

   Equal numbers do not mean equal content: `info.version` is an operator setting (default `1.0.0`) and does not move with every train. So always compare the paths as well.

5. Compare paths and methods. The command lists every operation as `METHOD /path`, and `diff` shows what is on only one side:

   ```bash
   diff <(jq -r '.paths | to_entries[] | .key as $p | .value | keys[] | "\(. | ascii_upcase) \($p)"' openapi-umgebung.json) \
        <(jq -r '.paths | to_entries[] | .key as $p | .value | keys[] | "\(. | ascii_upcase) \($p)"' "openapi-$(date +%F).json")
   ```

### Check required fields, responses, and stability

6. Check the required fields of the schemas you send. The list is in `required`:

   ```bash
   jq -c '.components.schemas.CreateSceneRequest.required' "openapi-$(date +%F).json"
   ```

   ```json
   ["name"]
   ```

7. Check the responses of the operations you use. The status codes are the keys under `responses`, and the error codes for them are in [The error format](/api/error-format):

   ```bash
   jq -c '.paths["/v1/scenes/{id}"].patch.responses | keys' "openapi-$(date +%F).json"
   ```

   ```json
   ["200","401","403","404","409","422","429","502"]
   ```

8. Check `x-stability` of the operations you use:

   ```bash
   jq -r '.paths["/v1/scenes/{id}"].patch["x-stability"]' "openapi-$(date +%F).json"
   ```

   The answer is `stable` or `beta`. The operation in the example is `stable`.

## Check the result

- `diff` in step 5 reports no line with `<` (only in your environment): no path that you call is removed or moved. Lines with `>` are new operations and break nothing.
- The `required` lists of the schemas you send contain no field that your requests do not send along.
- The status codes under `responses` of your operations are unchanged, and none carries `deprecated: true`. If `x-stability: beta` appears there, the shape can still move, see [Stability classes and sunset](/changes/stability-and-sunset).
- The dated file stays. At the next train, you reload and compare the two dated files with each other.

## If something goes wrong

| Symptom | Meaning | Solution |
|---|---|---|
| Your environment still returns the old state after a train | The response of `/v1/openapi.json` may be cached for five minutes (`Cache-Control: public, max-age=300`). | Reload after five minutes. |
| `info.version` is equal, `diff` still shows differences | The number in `info.version` does not follow the train. | Compare the content, not the number. The running version is named by `/health/version`. |
| A route is missing in both files but responds | It is classified as `internal` and therefore not part of the public surface, in neither of the two files. | Include only public operations in the integration. No promise covers an undocumented route. |
| An operation suddenly carries `deprecation` headers | It is being sunset. `x-sunset` in the description and `Sunset` in the response name the shutdown date. | Twelve months remain: switch the call to the named successor, see [Stability classes and sunset](/changes/stability-and-sunset). |
| `/health/version` responds with `429` | The route is limited to 60 calls per minute and address. | Wait the number of seconds named in `Retry-After`. |
| `/health/version` reports `unknown` or a number with `-dev` | The environment does not run from a release image. | For the comparison, use the specification, not the version. |

## Related

- [Release trains, notes, and deprecations](/changes/release-notes)
- [Read the reference](/api/read-reference)
- [Generate your own client from the specification](/api/generate-client)
