Upstream OpenAPI Release Impact Report for Pinned Clients
Pricing
from $30.00 / 1,000 run starteds
Upstream OpenAPI Release Impact Report for Pinned Clients
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
Pricing
from $30.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
Share
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
- Fetches the baseline document and the current document. Each hop of each redirect is checked again against the private-address rules.
- Parses JSON or YAML. YAML aliases are refused, so an alias bomb cannot expand.
- Indexes both documents: every operation (
GET /pets) and every schema (#/components/schemas/Pet, or#/definitions/Petfor Swagger 2). - Compares the two indexes and classes every difference.
- 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
GETrequests. No browser, no proxy, no authentication, no paid API, no LLM. - Only
httpandhttps. URLs with credentials,localhosttargets, 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
uv syncuv run pytestuv run ruff check .apify run --input-file .actor/default_input.json


