# Upstream OpenAPI Release Impact Report for Pinned Clients (`kingii98/upstream-openapi-release-impact-report-for-pinned-clients`) Actor

Compare a pinned OpenAPI or Swagger document with the current upstream document. The Actor reports every changed operation and every changed schema with a JSON pointer, the old value, the new value, a change class, and a breaking flag, and it answers one

- **URL**: https://apify.com/kingii98/upstream-openapi-release-impact-report-for-pinned-clients.md
- **Developed by:** [kingii98](https://apify.com/kingii98) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 run starteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Upstream OpenAPI Release Impact Report for Pinned Clients

Compare the **pinned** OpenAPI or Swagger document that your client was generated from with the
**current** upstream document, and get one answer: *is it safe to bump?*

The Actor reads two public documents with one `GET` each, builds a structural index of every
operation and every schema, and reports each difference with a JSON pointer, the old value, the new
value, a change class, and a breaking flag. No browser, no authentication, no proxy, no LLM.

### Who this is for

The maintainer of a generated or hand-written client SDK that wraps a fast-moving upstream API.
Before an upgrade, this person must know which upstream schema changes break the pinned decoder.

A worked example: a field `chunkTimeout` changes from `integer` to `integer | false` upstream. The
generated decoder still reads `int?`, so the first server that answers `false` throws at decode
time. The Actor calls that change `type-widened` and marks it breaking, with the exact pointer.

### What the Actor does

1. Fetches the baseline document and the current document. Each hop of each redirect is checked
   again against the private-address rules.
2. Parses JSON or YAML. YAML aliases are refused, so an alias bomb cannot expand.
3. Indexes both documents: every operation (`GET /pets`) and every schema
   (`#/components/schemas/Pet`, or `#/definitions/Pet` for Swagger 2).
4. Compares the two indexes and classes every difference.
5. Writes one summary record and one record for each changed operation and each changed schema.

### Input

Every field has a default, so a run with an empty input `{}` works and compares the two public
example documents.

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `baselineSpecUrl` | URL string | pinned petstore example | The pinned OpenAPI or Swagger document, for example a raw file at a fixed commit. |
| `currentSpecUrl` | URL string | pinned expanded petstore example | The current upstream document. |
| `scope` | array of strings | `[]` | Operation IDs, exact paths, or path prefixes that your client uses. |
| `breakingRules` | array of change classes | the nine classes marked **Yes** below | Which change classes count as breaking. |
| `maxDepth` | integer 1–12 | `8` | Maximum depth of the schema walk. |
| `maxChangesPerEntity` | integer 1–200 | `50` | Maximum number of changes listed in one record. |
| `timeoutSecs` | integer 2–120 | `30` | Per-document timeout. |
| `maxRedirects` | integer 0–20 | `5` | Redirect hops followed for each document. |
| `maxResponseBytes` | integer 1000–20000000 | `20000000` | Per-document body cap. |

#### Scope

`scope` is the list of things your client actually calls. An entry matches an operation when it is
equal to the `operationId`, equal to the path, or a prefix of the path. A schema is in scope when an
operation in scope refers to it, directly or through another schema.

With a scope, every record carries `usedByClient`, and `safeToBump` looks only at the records in
scope. A breaking change in an endpoint that your client never calls then keeps the bump safe.

Leave `scope` empty to keep every operation in scope; `usedByClient` is then `null`.

### Change classes

| Class | Meaning | Breaking by default |
| --- | --- | --- |
| `operation-removed` | An operation of the baseline is gone. | Yes |
| `operation-added` | An operation that the baseline does not have. | No |
| `operation-id-changed` | The `operationId` changed. A generated method is renamed. | No |
| `response-code-removed` | A documented response code is gone. | Yes |
| `response-code-added` | A new documented response code. | No |
| `field-removed` | A schema, a property, or a parameter is gone. | Yes |
| `field-added` | A new schema, property, or parameter. | No |
| `required-added` | A property, parameter, or request body became required. | Yes |
| `required-removed` | A property, parameter, or request body became optional. | No |
| `type-widened` | The type set grew, for example `integer` to `integer or boolean`. | Yes |
| `type-narrowed` | The type set shrank. | Yes |
| `type-changed` | The type set was replaced, for example `integer` to `string`. | Yes |
| `enum-value-removed` | An enum value is gone. | Yes |
| `enum-value-added` | A new enum value. | No |
| `ref-changed` | A `$ref` points at another schema. | Yes |

A widened type breaks a **decoder**, and a narrowed type breaks an **encoder**. Both are breaking by
default, because a pinned client is usually both. Use `breakingRules` to change the set.

An absent `type` means "any type", which is the widest type of all: `integer` to absent is a
widening, and absent to `integer` is a narrowing.

Only the top of an added or removed subtree is listed. The children of a new object are not repeated
as separate changes.

### Output

The dataset holds one `summary` record and one `operation` or `schema` record for each changed
entity.

Entity record: `recordType` (`operation` or `schema`), `entityId` (`GET /pets` or `Pet`),
`pointer`, `status` (`changed`, `added` or `removed`), `operationId`, `path`, `method`,
`schemaName`, `usedByClient`, `changeCount`, `breakingCount`, `breaking`, `changesTruncated`,
`changes`, `comparedAt`.

Each item of `changes` holds `pointer`, `changeClass`, `from`, `to` and `breaking`. A `from` or `to`
of `null` means that the value did not exist on that side.

Summary record: `ok`, `reasonCode`, `failedDocument`, `baselineSpecUrl`, `currentSpecUrl`,
`baselineTitle`, `baselineVersion`, `currentTitle`, `currentVersion`, `specFormat`,
`operationsCompared`, `schemasCompared`, `operationsChanged`, `schemasChanged`, `changeCount`,
`breakingCount`, `entitiesBreaking`, `entitiesBreakingInScope`, `changeCounts`, `walkTruncated`,
`scopeApplied`, `scopeSize`, `safeToBump`, `error`, `comparedAt`.

`safeToBump` is `true` only when both documents were read and no breaking change touches an entity
in scope.

#### Pointers

Pointers follow RFC 6901, with two stable extensions that survive reordering: a parameter is
`/paths/~1pets/get/parameters/{in}/{name}`, and a response is `/paths/~1pets/get/responses/{code}`.
The upstream document may list them in any order; the pointer stays the same.

#### Reason codes

`OK`, `HTTP_ERROR`, `NOT_A_SPEC`, `DOCUMENT_TOO_LARGE`, `TIMEOUT`, `DNS_FAIL`, `CONNECT_FAIL`,
`BLOCKED_TARGET`, `REDIRECT_LOOP`, `BAD_INPUT`.

`BAD_INPUT` means that the Actor refused the input before any request: a URL with a scheme that is
not HTTP(S), a URL with credentials, a URL with no hostname, a `localhost` target, two equal URLs,
or a field outside its bounds. The summary record then holds the two URL fields as they were given,
and `failedDocument` is `null`.

A document that cannot be read, a document that is not a spec, and an input that the Actor refuses
are **results**, not faults. The run writes a summary record with `ok: false`, sets a status
message, and **succeeds**. A failed run means a malfunction of the Actor itself.

### Pricing

Pay per event. Three events:

| Event | Unit | Counted |
| --- | --- | --- |
| `run-started` | One Actor run | Once, at the start of every run. |
| `spec-pair-diffed` | One baseline document compared with one current document | Once, and only when both documents were read, parsed and compared. A run that could not read a document is not charged for it. |
| `breaking-change-flagged` | One operation or schema marked breaking | Once for each entity record whose `breaking` is `true`. A record with ten breaking changes inside one schema is one event. |

The count of `breaking-change-flagged` never exceeds what the run's maximum total charge allows.

### Limits and safety

- Two HTTP `GET` requests. No browser, no proxy, no authentication, no paid API, no LLM.
- Only `http` and `https`. URLs with credentials, `localhost` targets, and hosts that resolve to a
  private or reserved address are refused, on the first request and on every redirect hop.
- The body of each document is capped, the schema walk is depth-capped and cycle-safe, and the
  change list of each record is capped.

### Local run

```bash
uv sync
uv run pytest
uv run ruff check .
apify run --input-file .actor/default_input.json
```

# Actor input Schema

## `baselineSpecUrl` (type: `string`):

Public URL of the pinned OpenAPI or Swagger document, for example a raw file at a fixed commit. JSON and YAML are both read. No authentication, no browser and no proxy. If you leave this field empty, the Actor compares the two public example documents shown below.

## `currentSpecUrl` (type: `string`):

Public URL of the current upstream OpenAPI or Swagger document. JSON and YAML are both read.

## `scope` (type: `array`):

Optional list of operation IDs, exact paths, or path prefixes that your client uses, for example 'listPets' or '/pets'. Every record then carries a usedByClient flag, and safeToBump looks only at the operations and schemas in scope. A schema is in scope when an operation in scope refers to it, directly or through another schema. Leave the list empty to keep every operation in scope.

## `breakingRules` (type: `array`):

Which change classes count as breaking for a pinned client decoder. The default is the set that makes a generated decoder throw: an operation or a field that disappeared, a response code that disappeared, a type that widened or narrowed, a new required field, a removed enum value, and a changed reference.

## `maxDepth` (type: `integer`):

Maximum depth of the schema walk. The walk stops below this depth, and the record reports walkTruncated.

## `maxChangesPerEntity` (type: `integer`):

Maximum number of changes listed in one operation or schema record. A record that holds more changes reports changesTruncated, and its counts stay complete.

## `timeoutSecs` (type: `integer`):

Per-document timeout for each GET. A document that exceeds it gets the TIMEOUT reason code.

## `maxRedirects` (type: `integer`):

Maximum redirect hops followed for each document. Every hop is checked again against the private-address rules.

## `maxResponseBytes` (type: `integer`):

Per-document body cap. A larger document gets the DOCUMENT\_TOO\_LARGE reason code and is not parsed.

## Actor input object example

```json
{
  "baselineSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore.json",
  "currentSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore-expanded.json",
  "scope": [],
  "breakingRules": [
    "operation-removed",
    "response-code-removed",
    "field-removed",
    "required-added",
    "type-widened",
    "type-narrowed",
    "type-changed",
    "enum-value-removed",
    "ref-changed"
  ],
  "maxDepth": 8,
  "maxChangesPerEntity": 50,
  "timeoutSecs": 30,
  "maxRedirects": 5,
  "maxResponseBytes": 20000000
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "baselineSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore.json",
    "currentSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore-expanded.json",
    "scope": [],
    "breakingRules": [
        "operation-removed",
        "response-code-removed",
        "field-removed",
        "required-added",
        "type-widened",
        "type-narrowed",
        "type-changed",
        "enum-value-removed",
        "ref-changed"
    ],
    "maxDepth": 8,
    "maxChangesPerEntity": 50,
    "timeoutSecs": 30,
    "maxRedirects": 5,
    "maxResponseBytes": 20000000
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingii98/upstream-openapi-release-impact-report-for-pinned-clients").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "baselineSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore.json",
    "currentSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore-expanded.json",
    "scope": [],
    "breakingRules": [
        "operation-removed",
        "response-code-removed",
        "field-removed",
        "required-added",
        "type-widened",
        "type-narrowed",
        "type-changed",
        "enum-value-removed",
        "ref-changed",
    ],
    "maxDepth": 8,
    "maxChangesPerEntity": 50,
    "timeoutSecs": 30,
    "maxRedirects": 5,
    "maxResponseBytes": 20000000,
}

# Run the Actor and wait for it to finish
run = client.actor("kingii98/upstream-openapi-release-impact-report-for-pinned-clients").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "baselineSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore.json",
  "currentSpecUrl": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/e77481c73e1c703c7de2e4249047b42b8e9b56f4/examples/v3.0/petstore-expanded.json",
  "scope": [],
  "breakingRules": [
    "operation-removed",
    "response-code-removed",
    "field-removed",
    "required-added",
    "type-widened",
    "type-narrowed",
    "type-changed",
    "enum-value-removed",
    "ref-changed"
  ],
  "maxDepth": 8,
  "maxChangesPerEntity": 50,
  "timeoutSecs": 30,
  "maxRedirects": 5,
  "maxResponseBytes": 20000000
}' |
apify call kingii98/upstream-openapi-release-impact-report-for-pinned-clients --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingii98/upstream-openapi-release-impact-report-for-pinned-clients"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/fJzEHfweQ4iZtreUa/builds/KUFELHXgTrywSic2y/openapi.json
