What API Ops is
Link to section “What API Ops is”API Ops in 30 seconds
Link to section “API Ops in 30 seconds”API Ops is the practice of changing APIs through a reviewed, automated pipeline. The API contract (an OpenAPI or AsyncAPI description) and the gateway configuration (routes, authentication, limits) live in Git. Each change is a pull request: gates lint it, compare it with the previous contract for breaking changes and run tests; after approval, a pipeline generates the gateway configuration and promotes the same commit through environments. Telemetry shows who calls which version. It serves API platform teams, backend engineers who publish APIs, and API product managers who promise consumers that an API will not break under them.
Where to start. Beginners: the glossary and Lab 1. Engineers: the contract gate and reference implementation. Leaders: the decision framework and judgement call.
At a glance
Link to section “At a glance”| Aspect | Summary |
|---|---|
| Goal | APIs that are consistent, discoverable and safe to evolve; gateways that match what Git says |
| People | API owner teams, an API platform team, API product managers, security reviewers, consumers |
| Prerequisites | HTTP and REST basics, YAML, Git and pull requests, one CI system; GitOps helps |
| In scope | Design-first contracts, lint rules, breaking-change detection, conformance and contract tests, gateway configuration as code, drift, versioning and deprecation, limits and quotas, OAuth 2.0 and keys at the gateway, catalogues and portals, API metrics and SLAs |
| Out of scope | Message flows between systems (IntegrationOps); API security testing (DevSecOps); running the authorisation server (IdentityOps); scope and permission design (IAMOps) |
| Outputs | Contract repository, ruleset, change gate, generated gateway configuration, consumer register, deprecation calendar, scorecard |
| Term status | Emerging (note above) |
The problem it solves
Link to section “The problem it solves”Hand-publishing works for one API. Trouble arrives with the tenth API, the fourth team and the first external partner:
- Four truths that disagree. Documentation, contract, gateway and code diverge when people fix production in a portal; Microsoft’s reference architecture requires such changes to be extracted back into Git through a reviewed pull request[2].
- Accidental breaking changes. A renamed field breaks clients that worked yesterday, and Hyrum’s Law warns that undocumented behaviour matters too: “With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.”[7]
- Forgotten versions. OWASP’s API9 (2023): attackers “usually get unauthorized access through old API versions or endpoints left running unpatched”[8].
- Inconsistent, undiscoverable APIs. Postman’s 2025 survey (over 5,700 developers, architects and executives) reports 55% struggling with inconsistent documentation and 34% unable to find existing APIs; the report does not state the base[9].
What API Ops is not: boundaries and naming
Link to section “What API Ops is not: boundaries and naming”- Not an API management product. Gateways and portals are tools; API Ops is the change process around them.
- Not GitOps renamed. OpenGitOps requires pull-based, continuously reconciled state (GitOps); the decK and APIOps
CLI pipelines push from CI (
deck gateway sync,apiops publish)[10][2]. Kubernetes controllers such as Tyk Operator allow a pull-based variant (tooling). - Not API security testing, which belongs to DevSecOps; API Ops runs the cheap contract checks.
- Not integration operations. Retries, dead-letter queues and replay belong to IntegrationOps. Editorial distinction: API Ops owns the contract; IntegrationOps owns the flow.
- Not the whole APIOps Cycles operating model, which also covers strategy, platform decisions, publishing and monitoring of API capabilities[6]; it complements this page and feeds it the first draft of a contract.
- Misconceptions: “we have a gateway, so we do API Ops” (a hand-configured gateway is the problem); “a version in the URL makes changes safe” (it only gives a breaking change somewhere to live).
Where it comes from and where it sits
Link to section “Where it comes from and where it sits”Origins and evolution
Link to section “Origins and evolution”- 2015: the Linux Foundation announced the Open API Initiative (5 November)[11]; OpenAPI “was originally based on the Swagger Specification, donated by SmartBear Software”[12].
- 2016–2018: an APIOps project for the National Library of Finland planned “APIOps Monthly Jam” sessions in 2016, and Nordic APIs describes Jarkko Moilanen as “creator of the APIOps concept”[5]. APIOps Cycles, “Developed since 2017”[6], was presented at the 2018 Platform Summit as a lean API design method[13].
- 2021: Kong described APIOps as applying “the solid and proven principles of DevOps and GitOps to the API and microservice lifecycle”, one of the earliest vendor uses we found[14].
- 2024–2025: Stripe moved to monthly non-breaking API releases from
2024-09-30.acacia[15]; RFC 9745 standardised theDeprecationheader (March 2025)[16]. - 2026: Microsoft’s reference architecture (dated 1 September) uses a single APIOps CLI and asks users of the legacy APIOps Toolkit to migrate[2]; the OpenAPI Initiative published Arazzo 1.1.0 (May), OpenAPI 3.2.1 and Overlay 1.2.0 (September)[17][18][19].
Glossary and mental model
Link to section “Glossary and mental model”Mental model: three artefacts, one loop. The contract says what the API promises; the gateway policy says how each environment exposes it; the consumer register says who depends on which version. Gates protect the register’s consumers from contract changes, the pipeline derives the policy from the contract, and telemetry keeps the register honest.
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 150, "nodeSpacing": 18, "rankSpacing": 30, "padding": 8}}}%%
flowchart TB
classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
C["1. Contract in Git: OpenAPI or AsyncAPI"]:::core
P{{"2. Gates: lint, breaking diff, tests"}}:::decision
G["3. Gateway policy generated per environment"]:::core
T["4. Telemetry per consumer and version"]:::neutral
R["5. Consumer register: who calls what"]:::neutral
C --> P
P -->|"pass"| G
P -->|"fail: fix, or add and deprecate"| C
G --> T --> R
R -->|"evidence for design and retirement"| C
Text equivalent: (1) A contract change in Git meets (2) the gates: lint, breaking diff, tests. On failure it returns as a fix or as an additive change that deprecates the old element. On success (3) the pipeline generates each environment’s gateway policy; (4) telemetry per consumer and version keeps (5) the consumer register current, the evidence for the next design or retirement.
| Term | Meaning |
|---|---|
| API gateway, API management | A gateway is the single entry point that routes calls to services and can check that clients are authorised[20]; API management products (Kong, Azure API Management, AWS API Gateway, Tyk) add limits, keys, portals and analytics |
| Consumer, developer portal | A client application calling the API; the portal is where its developers find documentation and get credentials |
| Contract, OAD | The machine-readable promise; an OpenAPI Description “formally describes the surface of an API and its semantics”[18]; AsyncAPI covers message-driven APIs[21] |
| Design-first, code-first | Contract before code (Zalando rule 100[22]), or generated from code or TypeSpec[23] |
| Ruleset | The style guide written as lint rules (Spectral, deck file lint)[24][25] |
| Breaking, additive change | A change existing clients cannot absorb, or can; each provider publishes its own list |
| Conformance test | Checks that the running service behaves as its contract says; Schemathesis generates such tests[26] |
| Three “versions” | The API’s version (v2, 2026-03-10), the OpenAPI version (openapi: 3.0.4) and info.version, which OpenAPI 3.2 says is distinct from both[18] |
| Deprecation, sunset | Still works but do not start using it; the date it is expected to stop responding. HTTP headers exist for both[16][27] |
| Stability level | Draft, alpha, beta or stable; sets how strictly oasdiff checks changes (x-stability-level)[28] |
| Rate limit, quota, token bucket | Short-window capacity protection versus a plan’s long-period allowance; a bucket refills at the rate and holds the burst[29] |
| Drift | Any difference between a deployed gateway and the approved commit |
| Consumer-driven contract | Consumer expectations verified against the provider (Pact)[30] |
How API Ops relates to other Ops
Link to section “How API Ops relates to other Ops”The boundaries below are editorial distinctions made by this Atlas (coverage matrix); no standard defines them.
| Neighbour | Shared ground | Choose that page when… |
|---|---|---|
| GitOps | State in Git, review, drift | You design reconciliation itself |
| IntegrationOps | Schemas, AsyncAPI | The problem is message delivery, retries or replay |
| DevSecOps | OWASP API Top 10 | You need active API scans or authorisation tests (its playbooks) |
| IdentityOps, IAMOps | OAuth 2.0 at the gateway | You run the authorisation server, or design scopes |
| TestOps | Contract tests | You run consumer-driven contract tests as a CI stage |
| PlatformOps | Developer portal, catalogue | The API platform is one product of a wider internal platform |
| SRE | API SLOs | You set SLO targets and error-budget policy |
| ReleaseOps | Canary, rollback | You choose the release strategy behind the API |
Principles and operating model
Link to section “Principles and operating model”Charter and principles
Link to section “Charter and principles”- The contract is source code. Microsoft says the API definition “should be considered “source code””[4].
- Generate gateway configuration from the contract, as Kong’s 2021 description does[14]; hand edits drift.
- Fail early. “The earlier in the pipeline you can identify deviations from your standards, the faster they are to resolve.”[14]
- Publish your compatibility policy, then make the tools match it. A gate enforces its configuration, not your intent (contract gate).
- One writer per target. Only the pipeline writes to production gateways, with separate identities for extracting and publishing; people need no standing write access[2].
- Own only what you can name. Scope each deployment to a team’s entities (tags, workspaces)[10].
- Retire with evidence. Deprecation is a measured migration, not an announcement.
Building blocks
Link to section “Building blocks”| Block | What it holds | Typical owner | Examples (October 2026) |
|---|---|---|---|
| Contract repository | Contracts, examples, changelog | API owner team | Git; OpenAPI 3.0–3.2[18], AsyncAPI 3.1[21] |
| Ruleset | House style and governance rules | API platform team | Spectral 6.17.0[24], deck file lint[25] |
| Change gate | Lint, breaking diff, tests | Platform team; owners act on results | oasdiff 1.33.0[31], Schemathesis[26], Pact[30] |
| Gateway policy | Routes, auth, limits, per-environment overrides | Platform team, with owners | decK 1.68.0[32], APIOps CLI 1.0.5[33] |
| Runtime | Gateways, portal or catalogue | Platform team | Kong, Azure API Management, AWS API Gateway; Backstage kind: API[34] |
| Evidence | Traffic per consumer and version, errors, 429s | Owners, product manager | Gateway logs and metrics[35] |
Roles, ownership and RACI
Link to section “Roles, ownership and RACI”| Activity | API owner team | API platform team | API product manager | Security | Consumers |
|---|---|---|---|---|---|
| Contract change | R/A | C | C | C (auth, data) | I (breaking or deprecating) |
| Ruleset and gate | C | R/A | C | C | — |
| Gateway policy (limits, auth) | R | A | C | C | I |
| Production promotion | R (requests) | A (pipeline) | I | I | — |
| Deprecation and sunset | R | C | A | I | C (consent for partners) |
| Emergency gateway change | R | A | I | I | I |
R = responsible, A = accountable, C = consulted, I = informed. A platform team runs ruleset, pipeline and gateways as a product while owner teams own contracts (PlatformOps); with few APIs, one team owns all. A board that hand-checks every change becomes a queue: automate its rules, keep it for exceptions.
Lifecycle: from draft to retirement
Link to section “Lifecycle: from draft to retirement”Diagram source (Mermaid)
stateDiagram-v2 [*] --> Draft Draft --> Beta Beta --> Stable Stable --> Deprecated Deprecated --> Retired Retired --> [*] Beta: Beta Beta: contract reviewed and mocked Stable: Stable Stable: owner, SLO, docs Stable: additive changes only Deprecated: Deprecated Deprecated: successor live, dates set Retired: Retired Retired: sunset passed, traffic gone
Text equivalent: Draft becomes Beta once the contract is reviewed and mocked; Beta becomes Stable with an owner, an SLO and documentation; Stable absorbs additive changes; it becomes Deprecated when a successor is live and dates are set, and Retired once the sunset has passed and traffic is gone. The table lists the gate and evidence for each transition.
| Transition | Gate (Atlas proposal) | Evidence |
|---|---|---|
| Draft → Beta | Lint passes; design review for new resources | Mock or test deployment; examples |
| Beta → Stable | Owner, SLO, docs, policy reviewed | x-stability-level: stable; catalogue entry |
| Stable → Stable | Breaking diff clean at error level | Changelog entry |
| Stable → Deprecated | Successor live; dates agreed | deprecated: true and x-sunset[28]; headers |
| Deprecated → Retired | Sunset passed; traffic gone or waived | Register: each consumer migrated or waived |
Operating model and governance
Link to section “Operating model and governance”| Cadence | Activity |
|---|---|
| Every pull request | Automated gates; code-owner review of contract and policy files |
| Weekly, 30 minutes | API health: errors, latency, 429s per consumer, drift, blocked merges |
| Monthly | Deprecation calendar: who still calls what, outreach, dates |
| Quarterly | Reconcile routes, contracts and catalogue; review ruleset; expire waivers |
Exceptions are reviewed waiver files with an owner and expiry (oasdiff reads --err-ignore and --warn-ignore
files[28]). Escalation: a blocked breaking change goes to the API product manager, who chooses between a new version
and a coordinated migration (judgement call).
Architecture and workflow
Link to section “Architecture and workflow”Reference architecture
Link to section “Reference architecture”A logical reference for several teams sharing a gateway platform; the shape is the same on Azure API Management or AWS.
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 210, "nodeSpacing": 20, "rankSpacing": 30, "padding": 8}}}%%
flowchart TB
classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
K["Git, one folder per API: contract, ruleset, gateway policy, overrides"]:::core
G{{"Pull-request gates: lint, breaking diff, conformance tests"}}:::decision
B["Merge build: generate and lint gateway configuration"]:::core
R["Release pipeline, sole writer: preview deletions, then sync"]:::neutral
D["Development and staging gateways"]:::core
P["Production gateways"]:::risk
T["Telemetry, catalogue, consumer register"]:::neutral
K --> G
G -->|"pass and approve"| B
B --> R --> D
D -->|"approval"| P
P --> T
P -.->|"nightly drift check"| R
T -.->|"evidence"| K
Text equivalent: Git holds, per API, the contract, ruleset, gateway policy and overrides. Pull requests pass gates (lint, breaking diff, conformance tests); after merge, a build generates and lints the gateway configuration. The release pipeline, the only writer, previews each change, deploys to development and staging, and after approval to production (the high-risk step). Production feeds telemetry, catalogue and consumer register, evidence for the contract; a nightly drift check compares production with the last deployment.
| Component | Key control |
|---|---|
| Contract repository, gates | Protected branch; code owners per API folder; required status checks |
| Release pipeline | Workload identity; approver reads the dry run’s deletions; one writer per target at a time[2] |
| Gateway | Admin interface on a private network; sync scoped by tags or workspaces |
| Catalogue and portal | Generated from the contract in CI; visible only to authorised users where needed[8] |
| Telemetry | Consumer identity on every request; retention policy |
Trade-offs. A gateway adds “Increased response time due to the additional network hop through the API gateway”, a cost the
same author calls insignificant for most applications, and another component to run[20]. Gateway features live either
in vendor extensions inside the contract (x-kong-*)[25] or in a separate policy file (this page’s choice). And reverting
an APIOps commit “doesn’t restore an incompatible or unavailable back end”[2].
The contract gate: what blocks a merge
Link to section “The contract gate: what blocks a merge”What counts as breaking is API Ops’s hardest policy call, and published policies disagree at the edges:
| Change | Google AIP-180[36] | GitHub REST[37] | Stripe[15] | Atlas gate default |
|---|---|---|---|---|
| Remove or rename an operation or field | Must not, within a major version | Breaking | Major releases only | Block unless deprecated and past sunset |
| Add a required parameter; make one required | Must not | Breaking | — | Block |
| Change a field’s type | Must not | Breaking | — | Block |
| Add a validation rule to a parameter | Stricter resource-name formats make old requests fail | Breaking | — | Block |
| Change a default value | “considered breaking and must not be done” | — | — | Block |
| Change authentication or authorisation requirements | — | Breaking | — | Block; security review |
| Add an optional parameter or a response field | May be added | Additive | Backward-compatible | Allow |
| Add a value to a response enum | “appropriate caution”; document that it may grow | Additive | — | Warn; allow if the enum is documented as open |
| Change ID length or format | Must not change format; treat larger size limits as incompatible | — | Backward-compatible (opaque strings) | Warn; allow only if documented as opaque |
A dash means the source does not address the change. oasdiff’s documentation states the pattern behind most rows: narrowing what requests accept, or widening what responses return, breaks clients[28]. Stripe puts the counterpart on consumers: “Design your integration to ignore unfamiliar fields and event types.”[15] Write down who carries which burden, then encode it:
- Error-level findings block the merge (
oasdiff breaking --fail-on ERRexits 1)[28]. - Warnings need a named reviewer’s acknowledgement.
- Deprecation needs a date: the house Spectral rule (an Atlas rule) rejects
deprecated: truewithoutx-sunset. oasdiff blocks removing an operation or parameter before its sunset, and with--deprecation-days-stablealso rejects a deprecation whose sunset is missing or too close[28]. - Stability sets strictness: oasdiff checks beta and stable by default, not draft or alpha[28].
- Waivers are files with owner and expiry (
--err-ignore), never a disabled check.
Tool defaults are a policy too. oasdiff 1.33.0 does not read this table. It rates removing an optional response property as
INFO, which oasdiff breaking never shows, and adding a response enum value as ERR, which blocks[28]. List every
check’s level with oasdiff checks changelog, then record your differences in a file passed with --severity-levels:
response-optional-property-removed err
response-property-enum-value-added warn
Removing a field after its sunset then needs a waiver line, which is the review you want. Zalando agrees that extending a closed
enum is incompatible; where values may grow, its rule 112 lists them under examples instead[22], so no enum finding arises.
End-to-end workflow
Link to section “End-to-end workflow”Diagram source (Mermaid)
---
config:
sequence:
actorMargin: 30
width: 80
diagramMarginX: 6
wrap: true
boxMargin: 6
messageMargin: 30
---
sequenceDiagram
autonumber
participant Team as Team
participant Git as Git
participant CI as Pipeline
participant GW as Gateways
Team->>Git: Pull request
Git->>CI: Run gates
opt Break found
CI-->>Git: Fail
Git-->>Team: Red check
Team->>Git: Deprecate
Git->>CI: Re-run
end
CI-->>Git: Pass
Team->>Git: Merge
Git->>CI: Build config
CI->>GW: Staging
CI->>GW: Preview
Note over Team,CI: Approver reads the planned deletions
CI->>GW: Sync prod
CI->>GW: Verify
Text equivalent: (1–2) A pull request triggers the gates. (3–6) Exception path: the breaking diff finds a removed field, so the team adds the new field, deprecates the old one and the gates re-run. (7–8) Gates pass; owners approve and merge. (9–10) The pipeline builds the configuration from the main branch and syncs staging. (11–12) It previews production; an approver reads the planned deletions and approves before the sync. (13) Verification (smoke tests and a drift diff) closes the release.
Every decision leaves a record: gate results, waivers, approvals and the commit deployed to each environment. In Microsoft’s version, “The release approver reviews the resources that the dry run creates, updates, deletes, or skips”, a dry run is “a deployment gate, not a substitute for automated API tests”, and only one pipeline writes to a target at a time[2].
Practices, frameworks and tools
Link to section “Practices, frameworks and tools”Core practices and playbooks
Link to section “Core practices and playbooks”Each playbook ends with an acceptance test and a rollback.
Playbook 1: the style guide as a ruleset
Link to section “Playbook 1: the style guide as a ruleset”- Start from published guidelines (Azure, Google AIPs, Zalando)[4][36][22]. Microsoft’s own Spectral ruleset is archived because “Azure API specifications have moved to TypeSpec”[38], so rulesets need owners.
- Encode 10–20 rules consumers feel (naming, RFC 9457 errors[39], pagination, security schemes, deprecation dates).
- Extend
spectral:oas; lower or switch off inherited rules withrule-name: warnoroff[40]. - Lint the contract locally and in the pull request, and the generated gateway file with
deck file lint[25].
Acceptance: every contract lints clean; every rule says why. Rollback: introduce new rules as warn for one cycle.
Playbook 2: the breaking-change gate
Link to section “Playbook 2: the breaking-change gate”- Publish the compatibility policy and the matching levels file (contract gate).
- In each pull request run
oasdiff breaking base.yaml head.yaml --fail-on ERR --severity-levels <file>[28]. - Set
x-stability-levelper operation; requiredeprecated: trueplusx-sunsetbefore removal[28]. - Test the running service against the contract (Schemathesis)[26]; Pact suits internal APIs with known consumers, not public APIs or pass-through gateways[30] (see TestOps).
Acceptance: a pull request removing a response field fails; one adding an optional field passes. Rollback: warn-only with a dated issue.
Playbook 3: versioning and deprecation
Link to section “Playbook 3: versioning and deprecation”Pick one scheme per API family and record it in an ADR; each has a price:
| Scheme | Who uses it | Strength | Price |
|---|---|---|---|
Major version in the path (/v1/) |
Google AIP-185[41] | Visible; easy to route | Whole-API version for one breaking change |
| Dated version in a header | GitHub, Stripe[37][15] | Small, opt-in changes | Provider keeps every supported shape |
| Media type; avoid versioning | Zalando rules 113–115[22] | Forces compatible evolution | Hard for simple clients |
| Versionless; deprecate fields | GraphQL guidance and @deprecated[42] |
No version sprawl | Needs per-field usage data |
Then run deprecation as a protocol:
- Mark the element
deprecated: truewith anx-sunsetdate in the contract[18][28]. - Send
Deprecation(a structured date such as@1796083199) andSunset(an HTTP date) headers and arel="deprecation"link to the migration guide; the sunset must not precede the deprecation[16][27]. - Measure traffic per consumer (Lab 4) and contact each; Zalando requires usage monitoring and client approval before shut-down[22].
- Choose the window: GitHub keeps a previous REST version “at least 24 more months”[37]; Meta guarantees each Graph API version at least two years of operation[43]; Google recommends 180 days between deprecating beta functionality and removing it[41].
- Decide post-sunset behaviour: an error such as
410 Gone, or Meta’s forwarding to “the next oldest, usable version”[43], which avoids outages but can change behaviour silently.
Acceptance: each deprecated element has a date, a guide and a per-consumer chart. Rollback: extend the sunset (RFC 8594 calls it a hint[27]); never shorten it without consent.
Playbook 4: rate limits and quotas
Link to section “Playbook 4: rate limits and quotas”- Rate limits protect capacity; quotas express a plan; provider spending limits stop bill shocks (OWASP API4)[44].
- Size the token bucket: from full, it admits up to burst + rate × T requests in T seconds, a target, not a guarantee[29].
- Key limits by consumer, not IP, when clients share proxies; Kong limits by consumer by default[45].
- Return
429withRetry-After[46];RateLimit-PolicyandRateLimitheaders are still an Internet-Draft (October 2026)[47]. - Expect approximation: AWS applies limits “on a best-effort basis”[29]; Azure’s rate limiting “is never completely
accurate” and counts per gateway[48]. Kong’s default
localpolicy “diverges when scaling the number of nodes”; its sharedclusterandredispolicies are accurate only with synchronous counter updates[45]. - Choose failure behaviour: Kong’s
fault_tolerantdefaults totrue, proxying without limits while the counter store is down[45].
Acceptance: a load test yields 429s with Retry-After at the expected rate per consumer. Rollback: change limits in Git only.
Playbook 5: authentication as gateway configuration
Link to section “Playbook 5: authentication as gateway configuration”- Keys identify; tokens authorise. AWS: “Don’t use API keys for authentication or authorization to control access to your APIs”; a key for one API in a usage plan works for every API in it[49].
- Validate JWT access tokens fully:
typat+jwt, exact issuer, audience naming this API, signature from the authorisation server’s keys, noalg: none, expiry[50]. Tokens “SHOULD be audience-restricted” and minimally privileged[51]. - Close side doors. Kong’s OpenID Connect plugin (Enterprise) reads bearer tokens from body, header and query string by default
(
bearer_token_param_type); Key Auth accepts query-string keys by default (key_in_query)[52]. Allow headers only: RFC 9700 says “Clients MUST NOT pass access tokens in a URI query parameter”, and recommends sender-constrained tokens (mutual TLS or DPoP), an option for partner APIs that IdentityOps would issue[51]. - Leave object-level checks to the service, which alone knows ownership (IAMOps designs scopes; DevSecOps tests authorisation).
Acceptance: tokens with wrong issuer, wrong audience, expired, or sent in a query string are refused in test. Rollback: revert the commit.
Playbook 6: drift and emergency changes
Link to section “Playbook 6: drift and emergency changes”- The pipeline is the only writer; people get read access and time-bound elevation[2].
- Scope every sync with
--select-tag: an unscoped sync deletes whatever the file lacks;deck gateway applynever deletes and suits bootstrap only[10]. - Schedule
deck gateway diff --non-zero-exit-code; exit code 2 means drift[53]. - Extract each emergency change and merge it through a pull request so Git stays authoritative[2].
Acceptance: a deliberate manual change turns the nightly drift job red. Rollback: re-sync the last approved commit per tag.
Methods, standards and frameworks
Link to section “Methods, standards and frameworks”| Item | Kind | Use in API Ops |
|---|---|---|
| OpenAPI 3.2.1, Overlay 1.2.0, Arazzo 1.1.0[18][19][17] | Specifications (OpenAPI Initiative) | Contract; separate variants; multi-call workflows |
| AsyncAPI 3.1.0[21] | Specification | Event and message contracts (flows: IntegrationOps) |
| RFC 9457, RFC 6585, RFC 9745, RFC 8594[39][46][16][27] | IETF RFCs (8594 is Informational) | Error format, 429, deprecation and sunset signals |
| RFC 9068, RFC 9700[50][51] | Standards Track; Best Current Practice | JWT access-token validation; OAuth security |
| OWASP API Security Top 10 (2023)[54] | Industry risk list | API9 inventory and API4 resource limits drive gates and limits |
| Google AIPs, Zalando and Azure guidelines[36][22][4] | Published company guidelines | Starting points for your ruleset; not standards |
| APIOps Cycles[6] | Community method (CC BY-SA 4.0) | Operating model around the pipeline: strategy, design, publishing, monitoring |
Only the RFCs and specifications are normative, for what they cover; company guidelines are optional. None defines “APIOps”.
Tooling landscape
Link to section “Tooling landscape”| Job | Options (October 2026) | Watch out for |
|---|---|---|
| Write contracts | Editors; TypeSpec emits OpenAPI 3.0 by default, 3.1 or 3.2 on request[23] | Generated contracts still need review |
| Lint | Spectral 6.17.0 (Node 22 or 24)[24]; deck file lint[25] |
Rulesets need owners; archived rulesets rot |
| Breaking diff | oasdiff 1.33.0[31]; Microsoft openapi-diff[4]; AsyncAPI CLI diff (6.2.1)[55] |
Pin versions; default severities are policy choices |
| Conformance, mocks | Schemathesis (OpenAPI 2.0–3.2)[26]; Prism (up to 3.1)[56] | Active tests mutate data: test systems only |
| Consumer contracts | Pact[30] | Not for public APIs or pass-through gateways[30] |
| Gateway as code (push) | decK 1.68.0 for Kong[32]; APIOps CLI 1.0.5 for Azure[33]; Tyk Sync[3]; Terraform or Bicep for infrastructure[2] | decK needs a database-backed Kong, not DB-less[57] |
| Gateway as code (pull) | Tyk Operator with Argo CD or Flux[58]; Kong Ingress Controller fed by deck file kong2kic[1] |
Needs Kubernetes; the controller, not the pipeline, corrects drift |
| Catalogue | Backstage kind: API with lifecycle deprecated[34] |
Catalogue entries drift unless generated |
Selection criteria: your gateways, per-team scoping, preview of deletions, offline CI use, and OpenAPI version support across
the whole chain. Not every tool lists OpenAPI 3.2 (Prism stops at 3.1[56]), so the labs use 3.0.4. AsyncAPI: the gate chain
above is OpenAPI-only; the AsyncAPI CLI’s diff classifies event-contract changes as breaking, non-breaking or
unclassified[55]. This Atlas did not test its rules: run it as a warning first and review event changes by hand.
Automation and AI
Link to section “Automation and AI”Automate the mechanical checks (style, compatibility, conformance, drift, inventory); keep humans for domain fit, whether a break is worth a new version, and migration timing: “Tools won’t catch all problems.”[4] AI assistants can draft descriptions or summarise a diff; their output passes the same gates. Agents are also becoming consumers: 51% of developers in Postman’s 2025 survey worry about unauthorised or excessive calls from AI agents[9]. Per-consumer identity, quotas and telemetry make such traffic visible.
API Ops in practice
Link to section “API Ops in practice”Reference implementation
Link to section “Reference implementation”A representative design, not a recipe: an orders API on Kong Gateway 3.16, decK 1.68, OpenAPI 3.0.4 and GitHub Actions;
hosts, names and dates are placeholders.
apis/orders/openapi.yaml contract (owners: orders team)
apis/orders/catalog-info.yaml catalogue entry that embeds the contract
apis/orders/gateway/plugins.yaml gateway policy layered onto the generated config
.spectral.yaml house ruleset (owner: API platform team)
.oasdiff-levels.txt severity levels matching the compatibility policy
.github/workflows/api-gate.yaml pull-request gates, preview and release
ops/kong-rules.yaml rules for generated gateway config (platform team)
CODEOWNERS /apis/orders/ @example/orders-team
1. The contract
Link to section “1. The contract”Example status: YAML syntax-checked by the Atlas validator; not linted with Spectral or diffed with oasdiff here.
openapi: 3.0.4
info:
title: Orders API
version: 2.3.0
contact: {name: Orders team, email: orders-team@example.com}
servers:
- url: http://orders-mock:4010
paths:
/v2/orders/{orderId}:
get:
operationId: getOrder
tags: [orders]
x-stability-level: stable
security: [{apiKey: []}]
parameters:
- {name: orderId, in: path, required: true, schema: {type: string}}
responses:
"200":
description: The order.
content:
application/json:
schema: {$ref: "#/components/schemas/Order"}
"404": {$ref: "#/components/responses/Problem"}
/v1/orders/{orderId}:
get:
operationId: getOrderV1
tags: [orders]
deprecated: true
x-sunset: "2027-06-30"
x-stability-level: stable
security: [{apiKey: []}]
parameters:
- {name: orderId, in: path, required: true, schema: {type: string}}
responses:
"200":
description: The order (legacy shape).
content:
application/json:
schema: {$ref: "#/components/schemas/Order"}
"404": {$ref: "#/components/responses/Problem"}
components:
securitySchemes:
apiKey: {type: apiKey, in: header, name: apikey}
responses:
Problem:
description: RFC 9457 problem details.
content:
application/problem+json:
schema: {$ref: "#/components/schemas/Problem"}
schemas:
Order:
type: object
required: [id, status]
properties:
id: {type: string, description: Opaque identifier.}
status:
type: string
description: Open set; clients must accept unknown values.
enum: [created, paid, shipped]
deliveryDate:
{type: string, format: date, deprecated: true, x-sunset: "2027-06-30"}
deliveredAt: {type: string, format: date-time}
Problem:
type: object
properties:
type: {type: string}
title: {type: string}
status: {type: integer}
detail: {type: string}
The API key identifies the consumer for limits and analytics; production authorisation adds OAuth 2.0 tokens (Playbook 5).
2. The house ruleset
Link to section “2. The house ruleset”Example status: YAML syntax-checked; rule syntax per the Spectral docs[40]; the JSONPath filters were not executed.
The ~ key selector and the numeric @property filter are documented jsonpath-plus syntax[59], untested under Spectral.
extends: ["spectral:oas"]
rules:
info-contact: error
operation-id-camel-case:
description: operationId is camelCase so generated clients read well.
severity: error
given: "$.paths[*][get,put,post,delete,patch].operationId"
then:
function: casing
functionOptions: {type: camel}
paths-kebab-case:
description: Path segments are kebab-case.
severity: warn
given: "$.paths[*]~"
then:
function: pattern
functionOptions: {match: "^(/[a-z0-9-]+|/[{][a-zA-Z]+[}])+$"}
errors-use-problem-json:
description: 4xx and 5xx responses use application/problem+json (RFC 9457).
severity: error
given: "$.paths[*][*].responses[?(@property >= 400 && @property < 600)].content"
then:
field: application/problem+json
function: truthy
deprecation-needs-sunset:
description: Deprecated operations, parameters and properties state x-sunset.
severity: error
given:
- "$.paths[*][?(@.deprecated == true)]"
- "$.paths[*][*].parameters[?(@.deprecated == true)]"
- "$.components.schemas[*].properties[?(@.deprecated == true)]"
then:
field: x-sunset
function: truthy
3. Gateway policy layered onto the generated configuration
Link to section “3. Gateway policy layered onto the generated configuration”Example status: YAML syntax-checked; file format from Kong’s add-plugins page[25]; plugin fields from the Rate Limiting and Key Auth references[45][52]; not applied to a gateway here.
add-plugins:
- selectors:
- $..services[*]
overwrite: false
plugins:
- name: key-auth
config:
key_names: [apikey]
key_in_query: false
- name: rate-limiting
config:
minute: 600
policy: redis
redis:
host: redis.internal.example
port: 6379
limit_by: consumer
fault_tolerant: false
fault_tolerant: false returns errors instead of proxying unlimited traffic when Redis is down[45]; choose it only if that
outage is cheaper than an unprotected backend.
4. Pull-request gates, preview and release
Link to section “4. Pull-request gates, preview and release”Example status: YAML syntax-checked; not run on GitHub. Action majors are the latest releases on 2026-10-18, all on Node 24,
which runners require since Node 20’s removal on 2026-09-23[60]; pin them by commit SHA in production. Assumes a
self-hosted runner that reaches the Admin API privately and has jq; Admin API credentials are deployment-specific and omitted;
the Admin API must never be reachable from the internet.
name: api-gate
on:
pull_request:
paths:
- "apis/**"
- "ops/**"
- ".spectral.yaml"
- ".oasdiff-levels.txt"
- ".github/workflows/api-gate.yaml"
push:
branches: [main]
paths: ["apis/**", "ops/**", ".spectral.yaml", ".github/workflows/api-gate.yaml"]
permissions:
contents: read
jobs:
gates:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
with: {fetch-depth: 0}
- uses: actions/setup-node@v7
with: {node-version: 24}
- uses: actions/setup-go@v7
with: {go-version: stable}
- name: Install pinned tools
run: |
npm install --global @stoplight/spectral-cli@6.17.0
go install github.com/oasdiff/oasdiff@v1.33.0
go install github.com/kong/deck@v1.68.0
- name: Lint contract
run: |
spectral lint apis/orders/openapi.yaml --ruleset .spectral.yaml \
--fail-severity error --format github-actions
- name: Breaking changes against the base branch
if: github.event_name == 'pull_request'
run: |
if ! git show "origin/${GITHUB_BASE_REF}:apis/orders/openapi.yaml" > base.yaml
then
echo "::notice::New API, no base contract: breaking diff skipped"
exit 0
fi
oasdiff breaking base.yaml apis/orders/openapi.yaml --fail-on ERR \
--severity-levels .oasdiff-levels.txt --deprecation-days-stable 180 \
--format githubactions
- name: Generate and lint gateway config
run: |
deck file openapi2kong --spec apis/orders/openapi.yaml \
--select-tag orders-api --output-file kong.generated.yaml
deck file add-plugins -s kong.generated.yaml \
apis/orders/gateway/plugins.yaml > kong.plugins.yaml
deck file add-tags -s kong.plugins.yaml orders-api > kong.yaml
deck file lint -s kong.yaml ops/kong-rules.yaml
- uses: actions/upload-artifact@v7
with: {name: kong-orders, path: kong.yaml}
preview:
if: github.event_name == 'push'
needs: gates
runs-on: [self-hosted, gateway-network]
env:
DECK_KONG_ADDR: ${{ vars.KONG_ADMIN_URL }}
steps:
- uses: actions/download-artifact@v8
with: {name: kong-orders}
- uses: actions/setup-go@v7
with: {go-version: stable}
- run: go install github.com/kong/deck@v1.68.0
- name: Plan production changes; stop above the deletion budget
run: |
deck gateway diff kong.yaml --select-tag orders-api --json-output > plan.json
jq -c '.summary' plan.json >> "$GITHUB_STEP_SUMMARY"
jq -r '.changes.deleting[] | "- delete \(.kind) \(.name)"' plan.json \
>> "$GITHUB_STEP_SUMMARY"
if [ "$(jq '.summary.deleting' plan.json)" -gt 3 ]; then
echo "::error::More than 3 deletions: split the change or approve a waiver"
exit 1
fi
- uses: actions/upload-artifact@v7
with:
name: release-orders
path: |
kong.yaml
plan.json
sync:
needs: preview
runs-on: [self-hosted, gateway-network]
environment: production
concurrency: {group: kong-production, cancel-in-progress: false}
env:
DECK_KONG_ADDR: ${{ vars.KONG_ADMIN_URL }}
steps:
- uses: actions/download-artifact@v8
with: {name: release-orders}
- uses: actions/setup-go@v7
with: {go-version: stable}
- run: go install github.com/kong/deck@v1.68.0
- name: Sync the reviewed plan if the gateway has not changed since
run: |
deck gateway diff kong.yaml --select-tag orders-api --json-output > now.json
a=$(jq -S '.changes' now.json | sha256sum | cut -d' ' -f1)
b=$(jq -S '.changes' plan.json | sha256sum | cut -d' ' -f1)
if [ "$a" != "$b" ]; then
echo "::error::Gateway changed after the reviewed preview; re-run"
exit 1
fi
deck gateway sync kong.yaml --select-tag orders-api
With required reviewers and self-review prevention set on the production environment, sync waits until someone other than the
person who triggered the run approves[60]; that reviewer reads the plan, deletions first, on the run’s summary page.
sync stops unless the reviewed change set is unchanged — the hash covers the whole list, so a same-count swap of deletions
stops the run too — and concurrency keeps one writer per target.
Real estates add a staging stage and signed artefacts; ops/kong-rules.yaml can require key-auth and rate-limiting on every
service. Pin tool tags you have tested[31][32].
Environments and secrets. Keep one generated file and put only what must differ in reviewed per-environment values, as
Microsoft’s override files do for “back-end URLs, resource IDs, and secret references”[2]. decK reads a state-file value
written as ${{ env "DECK_REDIS_PASSWORD" }} from the environment at sync time[32], so the pipeline injects it from its secret
store and Git holds none. The resolved value still reaches the gateway, so restrict who can read the Admin API too.
5. Catalogue entry from the contract
Link to section “5. Catalogue entry from the contract”Example status: YAML syntax-checked; fields from the Backstage descriptor reference[34]; not loaded into Backstage.
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: orders-api
spec:
type: openapi
lifecycle: production
owner: orders-team
definition:
$text: ./openapi.yaml
$text embeds the contract file itself, so the catalogue shows the merged contract rather than a copy that can drift; move
lifecycle to deprecated when the deprecation protocol starts.
Hands-on labs
Link to section “Hands-on labs”Labs 1–3 were not executed here (no third-party tools were installed on the Atlas build host); Lab 4 was. Use a laptop or a disposable VM, never a shared gateway.
Lab 1 (beginner): lint a contract against a house style
Link to section “Lab 1 (beginner): lint a contract against a house style”Objective. See style rules fail and pass. Prerequisites. Node.js 22 or 24; the contract and ruleset from the
reference implementation saved as openapi.yaml and .spectral.yaml. Cost and risk. None.
npx --yes @stoplight/spectral-cli@6.17.0 lint openapi.yaml --ruleset .spectral.yaml
echo "exit code: $?"
- Run it: expect no errors, possibly warnings from
spectral:oasdefaults such as missing descriptions. - Delete
x-sunsetfrom/v1/orders/{orderId}and run again. - Rename
getOrdertoget_orderand run again.
Expected output. Step 2 reports deprecation-needs-sunset, step 3 operation-id-camel-case, each with exit code 1 (default
fail severity error)[40]. Validation. With --fail-severity warn, warnings fail the run too. Troubleshooting.
Silent filter rule: test the JSONPath on a smaller file; engine error: Node too old. Teardown. Delete the folder.
Lab 2 (intermediate): predict, then detect breaking changes
Link to section “Lab 2 (intermediate): predict, then detect breaking changes”Objective. Calibrate your judgement against your policy and a tool. Prerequisites. oasdiff, pinned:
go install github.com/oasdiff/oasdiff@v1.33.0 — brew install oasdiff installs the latest release, and v1.34.0
(2026-10-18) changes default levels for checks this lab does not use[31]; the two-line levels file from the
contract gate saved as levels.txt. Copy openapi.yaml to base.yaml, then make
these edits in head.yaml one at a time. First predict each result:
| Edit | Your prediction |
|---|---|
A. Remove deliveryDate from Order |
|
B. Add optional query parameter expand |
|
C. Add a required query parameter fields |
|
D. Add cancelled to the status enum |
|
E. Remove /v1/orders/{orderId} today (sunset 2027-06-30) |
|
F. Change id from string to integer |
Show the reasoning for Lab 2
oasdiff breaking base.yaml head.yaml --fail-on ERR
echo "defaults, exit code: $?"
oasdiff breaking base.yaml head.yaml --fail-on ERR --severity-levels levels.txt
echo "policy levels, exit code: $?"
Expected output (from oasdiff 1.33.0’s rule levels, not executed here[28]; the six rules involved are unchanged in
v1.34.0’s source, checked 2026-10-19). With defaults: errors for C, D, E and F
(exit code 1) and nothing for A or B, because removing an optional response property is INFO and a new response enum value is
ERR. With levels.txt: errors for A, C, E and F, a warning for D (exit code 0) and nothing for B. The gap between your predictions,
the defaults and the policy is the lesson. Validation. Reverted edits give no findings. Troubleshooting. Nothing for E:
use an RFC 3339 sunset date[28]; nothing at all: check both files parse (oasdiff validate). Teardown. None.
Lab 3 (intermediate): gateway configuration as code with decK
Link to section “Lab 3 (intermediate): gateway configuration as code with decK”Objective. Generate Kong configuration from the contract, sync it, then detect drift. Prerequisites. Docker with Compose;
decK 1.68.0; the contract from the reference implementation in ./spec/openapi.yaml. decK needs a database-backed Kong, not
DB-less mode[57]. Cost and risk. Local only; ports bound to 127.0.0.1 (Kong’s own example binds all interfaces[61]).
Example status: YAML and bash syntax-checked; not executed. Image tags from Docker Hub on 2026-10-18; PostgreSQL 16 is in Kong 3.16’s tested range[61].
services:
db:
image: postgres:16
environment: {POSTGRES_USER: kong, POSTGRES_DB: kong, POSTGRES_PASSWORD: kong}
healthcheck:
test: ["CMD", "pg_isready", "-U", "kong"]
interval: 5s
migrate:
image: kong/kong-gateway:3.16.0.1
command: kong migrations bootstrap
environment: {KONG_DATABASE: postgres, KONG_PG_HOST: db, KONG_PG_PASSWORD: kong}
depends_on: {db: {condition: service_healthy}}
kong:
image: kong/kong-gateway:3.16.0.1
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: db
KONG_PG_PASSWORD: kong
KONG_ADMIN_LISTEN: "0.0.0.0:8001"
depends_on: {migrate: {condition: service_completed_successfully}}
ports: ["127.0.0.1:8000:8000", "127.0.0.1:8001:8001"]
orders-mock:
image: stoplight/prism:5.15.10
command: mock -h 0.0.0.0 /spec/openapi.yaml
volumes: ["./spec:/spec:ro"]
docker compose up -d && sleep 20
deck gateway ping
deck file openapi2kong --spec spec/openapi.yaml --select-tag orders-api \
--output-file kong.yaml
deck gateway diff kong.yaml --select-tag orders-api
deck gateway sync kong.yaml --select-tag orders-api
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/v2/orders/42
curl -s -o /dev/null -w "%{http_code}\n" -H "apikey: lab-key" \
http://127.0.0.1:8000/v2/orders/42
# Drift: change a route by hand, then ask decK what differs (exit code 2 = drift).
deck gateway diff kong.yaml --select-tag orders-api --non-zero-exit-code
echo "diff exit code: $?"
Expected output. The first diff lists entities to create. After sync, the first curl prints 401: the Prism mock, which
builds responses from the contract, fails requests whose declared security information is missing[56]. With any apikey
header it prints 200 (this lab adds no Key Auth plugin, so Kong does not check the key). Validation. Change a route’s paths through the Admin API: the
final diff exits 2[53] and sync restores it. Troubleshooting. 405 from the Admin API: DB-less mode; connection
refused: KONG_ADMIN_LISTEN or port bindings. Teardown. docker compose down -v removes containers and the database volume.
Lab 4 (advanced, executed): API KPIs from a gateway log
Link to section “Lab 4 (advanced, executed): API KPIs from a gateway log”Objective. Compute this page’s SLIs and deprecation evidence from gateway logs. Prerequisites. Python 3.8 or later; a JSON Lines extract with consumer, version, status, latencies and who produced the status. Cost and risk. Read-only; strip client IPs and tokens first.
Example status: executed on 2026-10-18 with Python 3.8.10 against a 20-line synthetic log written for this lab (revision r1); not run on real gateway logs. Percentiles checked by hand.
#!/usr/bin/env python3
"""API KPIs from gateway access logs in JSON Lines (standard library only).
Usage: python3 api_kpis.py access.jsonl [threshold_ms] [deprecated_versions]
One request per line, for example:
{"consumer": "acme-mobile", "version": "v2", "route": "GET /orders/{id}",
"status": 200, "latency_ms": 120, "upstream_ms": 105, "source": "upstream"}
"source" names who produced the status: "gateway" (auth, limits) or "upstream".
"""
import json
import math
import sys
from collections import Counter
def p95(values):
"""Nearest-rank 95th percentile (no interpolation)."""
ordered = sorted(values)
return ordered[math.ceil(0.95 * len(ordered)) - 1]
def load(path):
with open(path, encoding="utf-8") as handle:
return [json.loads(line) for line in handle if line.strip()]
def main():
rows = load(sys.argv[1])
threshold = float(sys.argv[2]) if len(sys.argv) > 2 else 300.0
deprecated = set((sys.argv[3] if len(sys.argv) > 3 else "v1").split(","))
total = len(rows)
if not total:
sys.exit("no requests in the log")
bad = sum(1 for r in rows if r["status"] >= 500)
throttled = Counter(r["consumer"] for r in rows if r["status"] == 429)
by_consumer = Counter(r["consumer"] for r in rows)
rejected = sum(1 for r in rows if r["source"] == "gateway")
# Latency only for proxied calls: fast gateway rejections would flatter it.
proxied = [r for r in rows if r["source"] == "upstream"]
old = Counter(r["consumer"] for r in rows if r["version"] in deprecated)
print(f"requests {total}")
print(f"availability SLI {(total - bad) / total:.2%} (5xx count as bad)")
if proxied:
fast = sum(1 for r in proxied if r["latency_ms"] <= threshold)
overhead = [r["latency_ms"] - r["upstream_ms"] for r in proxied]
print(f"latency SLI (<= {threshold:.0f} ms) {fast / len(proxied):.2%}"
f" ({len(proxied)} proxied)")
print(f"p95 latency {p95([r['latency_ms'] for r in proxied])} ms")
print(f"p95 gateway overhead {p95(overhead)} ms")
print(f"rejected at the gateway {rejected / total:.2%} (auth, limits)")
print(f"throttled (429) {sum(throttled.values()) / total:.2%}")
for consumer, count in throttled.most_common():
share = count / by_consumer[consumer]
print(f" {consumer}: {count} of {by_consumer[consumer]} ({share:.0%})")
print(f"deprecated-version share {sum(old.values()) / total:.2%}")
for consumer, count in old.most_common():
print(f" still calling {'/'.join(sorted(deprecated))}: {consumer} ({count})")
if __name__ == "__main__":
main()
Run python3 -I api_kpis.py access.jsonl 300 v1. Actual output on the synthetic log (20 requests; 2 server errors; 2
throttled and 1 unauthenticated request rejected by the gateway; 5 requests on v1):
requests 20
availability SLI 90.00% (5xx count as bad)
latency SLI (<= 300 ms) 82.35% (17 proxied)
p95 latency 520 ms
p95 gateway overhead 20 ms
rejected at the gateway 15.00% (auth, limits)
throttled (429) 10.00%
partner-shipco: 2 of 5 (40%)
deprecated-version share 25.00%
still calling v1: legacy-erp (4)
still calling v1: acme-mobile (1)
Validation. By hand: 18 of 20 requests are not 5xx (90%); 14 of 17 proxied requests take 300 ms or less (82.35%), while
counting the three fast rejections would have reported 85%. Interpretation. The availability SLI counts 429s as good, like
Google’s worked example[62], yet one partner lost 40% of its calls: report rejections per consumer beside it.
Troubleshooting. KeyError: map your gateway’s field names. Teardown. Delete the log extract.
Use cases and scenarios
Link to section “Use cases and scenarios”The first four are documented by the providers; the measures are this Atlas’s suggestions.
| Case | Context and method | Trade-off | Measure to watch |
|---|---|---|---|
| Stripe: dated versions | Version from the Stripe-Version header, SDK pin or account default; since 2024-09-30.acacia, monthly releases are non-breaking and twice-yearly majors may break[15]; webhooks migrate via a second endpoint[63] |
Provider keeps many shapes; consumers must ignore unknown fields | Traffic on current versions; failed webhook deliveries |
| GitHub: calendar versions | X-GitHub-Api-Version; unversioned calls get 2022-11-28; published breaking and additive lists[37] |
Two versions to run | Calls without a version header |
| Meta: forward, do not fail | Each Graph API version works at least two years; calls to an unusable version go to the next oldest usable one[43] | No hard outage, but silent behaviour change | Calls arriving on expired versions |
| Regulated estate on Azure (reference architecture, not a case study) | Regulated sectors named as use cases: review, dry run, one promoted commit, emergency changes extracted back[2] | Overhead per change | Production changes traceable to a reviewed commit |
| Internal platform, 40 APIs (illustrative) | One ruleset, a breaking gate, generated configuration, team tags | Waiver churn while legacy contracts are fixed | Blocked breaking merges; drift trending to zero |
Measure, govern, pay
Link to section “Measure, govern, pay”Metrics and KPIs
Link to section “Metrics and KPIs”| KPI | Definition and formula | Data source | Pitfall |
|---|---|---|---|
| Availability SLI | Non-5xx requests ÷ all requests, per API, 28 days[62] | Gateway logs | Counts 401 and 429 as good: pair it with rejections |
| Latency SLI | Proxied requests at or under a threshold ÷ proxied requests[62] | Gateway latency, which includes backend time[35] | Fast gateway rejections flatter it; averages hide the tail |
| Gateway rejections per consumer | Gateway-produced 401, 403, 429 ÷ that consumer’s requests | Gateway logs | Invisible in API-wide totals |
| Breaking-change escape rate (Atlas KPI) | Consumer incidents caused by contract changes ÷ contract changes merged, per quarter | Incident tags, Git | Needs honest tagging |
| Gate catch rate (Atlas KPI) | Breaking changes blocked ÷ (blocked + escaped) | Gate results, incidents | Read with waiver counts |
| Contract drift | Entities differing between live gateways and the approved commit | Nightly diff[53] | Covers tagged entities only |
| Inventory coverage | Routes with a contract and owner ÷ all routes (API9)[8] | Gateway export, catalogue | Misses APIs outside the gateway |
| Deprecated traffic | Requests and consumers per deprecated version | Gateway logs (Lab 4) | Anonymous consumers cannot be contacted |
| Time to first call | Discovery to first successful call (public APIs)[64] | Portal and gateway events | “Be careful of artificially hacking a TTFC”[64] |
Worked calculation (illustrative month). An API serves 48,600,000 requests; 31,590 are 5xx. Availability = 1 − 31,590 ÷
48,600,000 = 99.935%. Against a 99.9% SLO the month allows 48,600 bad requests, so 65% of the error budget is spent (see
SRE for budget policy). 291,600 requests (0.6%) were throttled, but one partner had 18% of its calls throttled:
a wrong limit or a client retrying without backoff. Version v1 carried 1,652,400 requests (3.4%) from seven consumers, 71%
from one, so one conversation decides the sunset. Over the quarter, 140 contract changes merged and
2 caused consumer incidents: escape rate 1.4%; the gate blocked 23, so catch rate = 23 ÷ 25 = 92%.
Review the first three weekly, the rest monthly; set targets after a baseline quarter. SLO or SLA? An SLO is your internal target; an SLA is “an explicit or implicit contract with your users that includes consequences of meeting (or missing) the SLOs they contain”[65]. Promise partners an SLA looser than the SLO you meet, measured the same way, and never present rate limits, which AWS calls targets, as guaranteed throughput.
Quality, security, privacy and compliance
Link to section “Quality, security, privacy and compliance”- Inventory first: every exposed host and version with owner and data sensitivity; “Include the documentation build in your CI/CD pipeline”; protect old versions or retire them[8].
- Resource limits on payload size, page size, execution time and paid downstream calls (API4)[44].
- Credentials: keys identify, tokens authorise (Playbook 5); keep secrets out of contracts and generated files, and inspect extracted artefacts for them[2].
- The control plane is production: admin interfaces on a private network (decK passes credentials with
--headers[32]); separate least-privilege identities for extracting and publishing; workload identity where available[2]. - Privacy: synthetic data in examples and mocks; set retention and access for gateway logs, which hold consumer identifiers and IP addresses.
- Compliance evidence: commit, approval, gate results and deployed commit form a change record auditors can sample; whether it satisfies a framework is for your compliance function (ComplianceOps).
Cost, capacity and sustainability
Link to section “Cost, capacity and sustainability”- Gateway and pipeline costs depend on tier, units, regions and usage; Microsoft gives no fixed estimate[2]. AWS charges extra for per-method metrics[35].
- Limits are not budgets. AWS: “Don’t rely on usage plan quotas or throttling to control costs or block access to an API.”[49] Use billing alerts and provider spending limits[44].
- Capacity arithmetic. Rate 50 per second, burst 100: the token-bucket bound for one minute from a full bucket is 100 + 50 ×
60 = 3,100 requests, which AWS treats as a target, not a ceiling[29]. With Kong’s per-node
localcounters[45], a 600-a-minute consumer behind four round-robin nodes can get up to 4 × 600 = 2,400 (illustrative). - Version sprawl is the hidden cost: “Running multiple versions of an API requires additional management resources”[8]; retiring versions is also the sustainability lever.
Assurance of the operating model
Link to section “Assurance of the operating model”Healthy signals: zero drift findings; no human writes to production gateways except recorded emergencies; inventory coverage near 100%; no expired waivers; each deprecated version dated with falling per-consumer traffic. Monthly, trace one merged change from pull request to deployed configuration.
When things go wrong
Link to section “When things go wrong”Common failures and troubleshooting
Link to section “Common failures and troubleshooting”| Symptom | Likely cause | Diagnosis | Fix and prevention |
|---|---|---|---|
| A consumer breaks after a “compatible” release | New enum value, tighter validation, changed default (Hyrum’s Law) | Diff the contracts; sample responses | Roll back or shim; document open enums |
| Another team’s routes vanish after a deploy | Sync without select tags | deck gateway diff --json-output shows deletions[53] |
Runbook below |
| One consumer suddenly gets many 429s | Limit keyed by IP behind a shared proxy; lowered limit; client retry storm | Plugin diff in Git; limit_by; per-consumer rate |
Key by consumer; Retry-After; client backoff |
| Rate limits silently stop | Counter store down with fault_tolerant: true[45] |
Plugin errors in gateway logs | Restore the store; alert on it; decide fail-open or fail-closed |
| Live configuration differs from Git | Hand edits in a portal or Admin API | Drift job exit code 2 | Extract and merge, or re-sync; remove standing write access |
| Gate blocks every pull request after a tool upgrade | New checks or levels in a new linter or diff version | Compare versions in CI logs | Pin versions; upgrade in its own pull request |
Runbook: a release deleted another team’s routes
Link to section “Runbook: a release deleted another team’s routes”Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 160, "nodeSpacing": 16, "rankSpacing": 26, "padding": 8}}}%%
flowchart TB
classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
A["404 spike on paths owned by another team, minutes after a release"]:::risk
B["Freeze: pause all gateway pipelines"]:::core
C{{"Did the release log or diff report deletions?"}}:::decision
D["Re-sync each affected team's last approved commit with its own tag"]:::core
E["Look elsewhere: backend or DNS change, expired certificate"]:::core
F["Verify: drift diff clean for every tag; 404 rate back to baseline"]:::core
G["Prevent: mandatory select tags; owner-tag lint; deletion budget in the preview"]:::core
A --> B --> C
C -->|"yes"| D --> F --> G
C -->|"no"| E
Text equivalent: 404s spike on another team’s paths after a release. Freeze all gateway pipelines and check whether the release reported deletions. If so, re-sync each affected team’s last approved commit with its own tag, verify clean drift diffs and baseline 404 rates, then add mandatory tags, an owner-tag lint rule and a deletion budget. If not, look at backend, DNS or certificates.
Worked runbook (illustrative). 14:02, the payments release ran deck gateway sync payments.yaml without --select-tag;
decK deleted the orders and catalog routes, which were not in the file[10]. 14:06, the 404 alert fires.
- Contain (5 minutes). Pause every gateway pipeline; name an incident owner.
- Confirm. The release log lists deletions;
deck gateway diff orders.yaml --select-tag orders-apifrom the orders team’s last approved commit lists entities to create[53]. - Recover. Sync each affected team’s last approved artefact with its own tag, not by hand in the Admin API (new drift).
- Verify. Clean drift diff for every tag; 404 and 5xx rates at baseline; smoke tests pass.
- Prevent. Mandatory
--select-tagin the shared release template, an owner-tag lint rule, and the preview job’s deletion budget (release workflow). - Review. Blameless review with owned actions (IncidentOps).
Anti-patterns and myths
Link to section “Anti-patterns and myths”- Portal-first, export later: the contract documents the past. Git first; extract only to bootstrap.
- Contract as documentation only: if no gate reads it, it drifts.
- The 200-rule ruleset: hundreds of warnings teach people to ignore them. Few rules, each with a reason.
- A major version per change:
/v7/means six sets of tests and docs. Prefer additive change and deprecation. - Deprecation by email: without headers, dates and per-consumer traffic, sunsets slip forever.
- Business logic in the gateway: transformations hide behaviour outside the contract and the service’s tests.
- Quotas as budgets, keys as authentication (AWS warns against both[49]).
- Myth: design-first is waterfall. A contract can be written in an hour, mocked, and changed many times before code exists.
- Myth: GraphQL needs no governance. It avoids versions[42] but still needs deprecation, usage tracking and limits.
Deciding and adopting
Link to section “Deciding and adopting”When to use and when not to use
Link to section “When to use and when not to use”Use it when several teams share gateways, when consumers you do not control (partners, customers, mobile apps) depend on your contracts, when changes are frequent, or when you must show who approved each production change. Microsoft frames the decision around ownership, review requirements and deployment boundaries, which “don’t have fixed thresholds”[2].
Do not build the full pipeline for one internal API with two known consumers that changes twice a year: a contract in Git, a linter and a reviewed pull request suffice; Microsoft advises small, slow-changing estates to “begin with a manual pull request workflow”[2]. Skip gateway-as-code without a gateway, and breaking gates for drafts nobody calls. If infrastructure and API configuration always change together, one IaC pipeline may serve both[2]. Hidden responsibility: someone owns the ruleset, levels file, waivers and consumer register indefinitely.
Decision framework
Link to section “Decision framework”Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 150, "nodeSpacing": 16, "rankSpacing": 26, "padding": 8}}}%%
flowchart TB
classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
Q1{{"Do consumers outside the owning team depend on the API?"}}:::decision
N1["Contract in Git plus lint in CI; revisit yearly"]:::neutral
QG{{"Is there an API gateway in front of it?"}}:::decision
Q2{{"External consumers, several teams on one gateway, or weekly changes?"}}:::decision
L2["Breaking-change gate, deprecation dates, per-consumer telemetry"]:::core
L3["Full pipeline: generated config, scoped sync, drift checks, sole writer"]:::core
Q1 -->|"no"| N1
Q1 -->|"yes"| QG
QG -->|"no"| L2
QG -->|"yes"| Q2
Q2 -->|"no"| L2
Q2 -->|"yes"| L3
Text equivalent: If no one outside the owning team depends on the API, keep the contract in Git with linting in CI and revisit yearly. If others depend on it but no gateway fronts it, add a breaking-change gate, deprecation dates and per-consumer telemetry. With a gateway, adopt the full pipeline (generated configuration, scoped sync, drift checks, the pipeline as sole writer) when consumers sit outside your organisation, several teams share the gateway or it changes weekly; otherwise take the lighter option.
Maturity model
Link to section “Maturity model”This is this Atlas’s model, not an industry standard.
| Level | Observable behaviour | Exit criterion |
|---|---|---|
| 1. Ad hoc | APIs configured in portals; specs exported, if at all | Every gateway API has a contract in Git |
| 2. Described | Contracts in Git; documentation generated in CI | Ruleset runs on every pull request |
| 3. Gated | Lint and breaking diff block merges; waivers are files | Zero ungated contract changes for a quarter |
| 4. Reconciled | Pipeline-only, team-scoped gateway sync; nightly drift check | No unrecorded human writes to production |
| 5. Evidence-led | Consumer register; deprecation by traffic data; API SLOs and scorecards | Every deprecated version dated, consumer count falling |
Adoption roadmap: 30/60/90 days
Link to section “Adoption roadmap: 30/60/90 days”| Window | Actions | Milestone |
|---|---|---|
| Days 1–30 | Inventory routes and owners; two pilot contracts in Git; 10-rule ruleset in warn mode; versioning ADR | Coverage measured; pilots linted |
| Days 31–60 | Rules to error; breaking gate; generated configuration and team tags for pilots; nightly drift job | A break caught in review; drift visible |
| Days 61–90 | Pipeline is sole production writer for pilots; per-consumer dashboards; deprecation calendar; next five APIs | 30 days without manual production edits |
Toolkit
Link to section “Toolkit”Checklists
Link to section “Checklists”Preflight for a new API
- Contract in Git, design reviewed, linted clean
- Versioning scheme and compatibility policy referenced
- Owner, consumers, stability level and SLO recorded
- Error format RFC 9457; size and page limits set
- Authentication scheme, scopes and rate limits reviewed
Pull-request review
- Gates green; any waiver has an owner and expiry
- Warnings acknowledged by a named reviewer
- Deprecations carry a sunset date and migration note
- Gateway policy diff read, especially deletions
Deprecation and sunset
- Successor live and documented
- Deprecation and Sunset headers configured;
rel="deprecation"link - Every remaining consumer identified and contacted
- Post-sunset behaviour (error or forwarding) tested
Quarterly audit
- Inventory reconciled: gateway, contracts, catalogue
- Drift findings and manual-write audit reviewed
- Ruleset and tool versions reviewed; expired waivers removed
Templates
Link to section “Templates”Pull-request template for an API change
API: <name> Stability: draft | beta | stable
Change type: additive | deprecating | breaking (needs version + approval)
Consumers affected: <list or "none known"> Evidence: <dashboard link>
Gate results: lint <pass/fail> breaking diff <none/WARN/ERR> tests <pass/fail>
Waivers: <file, owner, expiry> or none
Gateway policy changes: <limits, auth, routes; deletions?>
Rollback: <revert commit; backend compatibility checked? yes/no>
Deprecation notice
Subject: <API> <version or operation> deprecated on <date>, sunset on <date>
What changes: <element> is replaced by <successor>. Migration guide: <link>
Why: <one sentence>
Your traffic: <consumer> made <n> calls to the deprecated element in the last 30 days
Signals: responses carry Deprecation and Sunset headers from <date>
After sunset: requests return <status> | are forwarded to <version>
Contact: <team channel>; request an extension before <date>
Architecture decision record: versioning scheme
Context: consumers (internal/partner/public), change rate, gateway capabilities
Options: URL major version | dated header | media type | versionless + deprecation
Decision: <option>; compatibility policy: <link>
Support window: previous version kept <n> months after successor release
Consequences: maintained shapes, test matrix, routing rules, docs
Review date: <date>
API scorecard (monthly, per API)
Availability SLI ...... 99.__ % Latency SLI ....... __ %
Gateway rejections .... __ % worst consumer: <name> __ %
Contract drift ........ __ entities Inventory coverage __ %
Breaking escapes ...... __ this quarter Waivers open: __ (oldest __ days)
Deprecated traffic .... __ % from __ consumers next sunset: <date>
Deep comparisons
Link to section “Deep comparisons”| Choice | Option A | Option B | Prefer A when… |
|---|---|---|---|
| Contract authoring | Design-first | Code-first (from code or TypeSpec[23]) | Outside consumers must review the design early |
| Gateway configuration | Gateway-native tool (decK, APIOps CLI) | General IaC (Terraform, Bicep) | Policies change faster than infrastructure or have other owners[2] |
| Compatibility evidence | Contract plus conformance tests | Consumer-driven contracts (Pact) | Consumers are unknown or public[30] |
| Gateway-specific settings | Separate policy file or overlay[19] | Vendor extensions in the contract[25] | The contract is published externally |
Do we need a gateway to practise API Ops? No. Contracts in Git, linting and breaking-change gates help any API. Gateway-as-code applies only where a gateway exists.
We have hundreds of APIs and no contracts. Where do we start? Extract what the gateways know (Microsoft’s extract-first path[2]), commit it as a reviewed baseline, gate only new changes, and add real contracts as each API next changes.
Does this apply to GraphQL or gRPC? Yes, with other tools. GraphQL evolves a versionless schema[42]; Google’s AIP-180 states its compatibility rules in Protocol Buffers terms (fields, messages, oneofs)[36]. Gates, scoped deployment and drift checks stay the same.
Where should the contract live: with the code or in a central repository? With the code when one team owns both and releases them together; central (or mirrored) when a platform team must lint and gate all APIs. Either way, one file is the source of truth.
Is this just GitOps? It borrows Git as source of truth and drift detection, but the decK and APIOps CLI pipelines push rather than reconcile continuously; Kubernetes controllers such as Tyk Operator allow the pull model (GitOps explains it).
Going further
Link to section “Going further”Advanced topics and emerging direction
Link to section “Advanced topics and emerging direction”- Established now: OpenAPI 3.2 adds a
queryoperation for the HTTP QUERY method and streaming media types[18]; Overlay 1.2.0 yields audience- or gateway-specific variants from one contract[19]. - Maturing: Arazzo 1.1.0 describes multi-call workflows for tests and documentation[17]; RateLimit headers are still a draft[47].
- Early: AI agents as consumers[9] and contract-aware review assistants; treat vendor claims as marketing until tested.
- Kubernetes: Gateway API (
Gateway,HTTPRoute) is “focused on L4 and L7 routing”[66]; quotas, keys and portals come from your gateway product.
Learning path
Link to section “Learning path”| Stage | Practice |
|---|---|
| Prerequisites | HTTP semantics, JSON Schema basics, Git pull requests |
| 1 day | Read one contract end to end; do Lab 1 |
| 1 week | Labs 2–4; write your compatibility policy using the contract gate table |
| 30 days | Run the 30-day roadmap on one real API; add per-consumer telemetry |
| Advanced | Multi-team scoped sync, deprecation of a partner-facing version, SLOs with SRE |
| Portfolio idea | A public repository with a contract, ruleset, gate workflow and a written deprecation of one operation |
Wrap-up
Link to section “Wrap-up”Executive recap and cheat sheet
Link to section “Executive recap and cheat sheet”| Topic | Remember |
|---|---|
| Term | APIOps: two related meanings, no standard; here, the pipeline |
| Core artefact | The contract, linted and diffed in every pull request |
| Gate | Errors block; warnings need a reviewer; deprecations need dates; tool levels match the policy |
| Gateway | Generated configuration; sole-writer pipeline; scoped, previewed sync; drift checks |
| Limits | Approximate; not budgets, not authentication |
| Retirement | Headers, dated sunset, per-consumer traffic, partner consent |
| Metrics | SLIs, rejections per consumer, drift, escapes, deprecated traffic |
A judgement call: a new major version or a twelve-month migration?
Link to section “A judgement call: a new major version or a twelve-month migration?”Situation (illustrative). Orders v1 has 30 consumers (24 internal, 6 partners). Its decimal amount in major currency units
causes rounding disputes; the fix is an integer amount in minor units plus a currency code. Engineers want 12 other clean-ups too.
Option A: launch v2 with all 13 changes; sunset v1 in 18 months. Option B: add amountMinor and currency to v1
(additive, so the gate passes), deprecate amount with a 12-month sunset, and migrate consumer by consumer.
Assumptions. Three internal consumers carry 70% of traffic; partners carry 20% and release quarterly; consumers ignore unknown fields, as Stripe asks of its integrators[15] (verify with tests).
Why the attractive choice can be wrong. Option A looks clean but asks 30 consumers to absorb 13 changes for one fix. Quarterly
partners slip, v1 outlives its sunset, and the team runs two full versions, the sprawl that costs “additional management
resources”[8]. Option B asks each consumer to change one field and turns migration into a per-consumer chart.
Recommendation. Option B, unless the resource model itself must change (then a new version is honest); bundle clean-ups only when consumers must migrate anyway. Uncertainty: typed partner SDKs may need a release to read the new field, and a partner that cannot move in 12 months forces an extension.
Learning objectives and mastery check
Link to section “Learning objectives and mastery check”You should now be able to explain what APIOps means and why it is unstandardised; write a compatibility policy and make the tools enforce it; design a pipeline that generates and scopes gateway configuration; measure rejections and deprecated traffic per consumer; and decide between a new version and an additive migration.
- A manager says: “We run Kong, so we do APIOps.” What would you check?
Show answer for question 1
- Under this page’s gate defaults, which block: (a) a new optional parameter, (b) an optional parameter made required, (c) a new response enum value, (d) a smaller default page size?
Show answer for question 2
(b) and (d): GitHub lists making a parameter required as breaking[37]; with a smaller default page size, Google notes, older clients “will incorrectly assume all results were returned”[36]. (a) is additive; (c) warns, allowed only for enums documented as open, because GitHub and Google differ. oasdiff blocks (c) by default until your levels file lowers it[28].
- A shared Kong gateway is updated by
deck gateway sync team.yamlfrom five repositories. What is the risk and the fix?
Show answer for question 3
- The API-wide 429 rate is 0.6%, but one partner sees 18%. Name two likely causes and the next step.
Show answer for question 4
- A colleague proposes AWS usage-plan API keys as the access control for a partner API. Your answer?
Show answer for question 5
- A team sets
Deprecation: @1798761600(1 January 2027) andSunset: Tue, 01 Dec 2026 00:00:00 GMT. What is wrong?
Show answer for question 6
The sunset precedes the deprecation date, which RFC 9745 forbids[16]. Move the sunset later and give consumers a real window.
Conclusion
Link to section “Conclusion”API Ops has no standard and no governing body; it is a vendor-popularised name for an idea that holds up anyway: an API is a promise, and promises need a change process. Put the contract in Git, decide in writing what counts as breaking, let a pipeline that nobody bypasses generate and scope gateway configuration, and retire versions with evidence about who still depends on them. The checks run on open-source tools; the hard part is the policy, and that is where this page asks you to spend your effort.
Sources and further reading
Link to section “Sources and further reading”All sources were opened on 2026-10-18; dates are the sources’ own. Claim-level notes and gaps are in research/api-ops-ledger.md.
Changelog. 2026-10-18, r0: first draft (research, ledger, six diagrams). 2026-10-18, r1: after independent review
reviews/api-ops-r1.md: oasdiff levels file and corrected Lab 2; preview, deletion budget, approval and concurrency in the
release; pull-based operators; both meanings and 2016–2018 history of “APIOps”; AIP-180 cells; current action majors; Lab 4
latency over proxied calls (re-executed); portal, SLA, AsyncAPI and secrets notes; glossary; Figures 2, 4 and 6 redrawn.
2026-10-19, post-score polish after reviews/api-ops-score.md (F1–F5): Meta guarantee reworded as a paraphrase (the page’s two
guarantee sentences are recorded in the ledger); the Spectral ruleset status line names the untested jsonpath-plus constructs;
Lab 2 pins oasdiff instead of brew install and records that the six rules it uses are unchanged at v1.34.0; the release
re-diff guard hashes the change list instead of comparing summary counts; the Node 20 footnote date is explained.
APIOps with decK and Kong Ingress Controller — Kong documentation, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
Automate API Management configuration deployments by using APIOps CLI — Azure Architecture Center, 2026-09-01. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Embrace efficiency and automation with APIOps and Tyk — Tyk, updated 2026-01-30. Checked 2026-10-18. ↩︎ ↩︎
Use DevOps and CI/CD to publish APIs — Microsoft Learn, updated 2026-06-25. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
APIOps Community; APIOps/Digiapi (2016 roadmap); Jarkko Moilanen’s author page — apiops.info, GitHub, Nordic APIs. Checked 2026-10-18. ↩︎ ↩︎
APIOps Cycles and its licensing page — APIOps Cycles community, coordinated by Osaango, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
Hyrum’s Law — Hyrum Wright, undated. Checked 2026-10-18. ↩︎
API9:2023 Improper Inventory Management — OWASP, 2023. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
2025 State of the API Report — Postman, 2025. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
deck gateway sync and deck gateway commands — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
New Collaborative Project to Extend Swagger Specification — Linux Foundation, 2015-11-05. Checked 2026-10-18. ↩︎
About the OpenAPI Initiative — OpenAPI Initiative, undated. Checked 2026-10-18. ↩︎
APIOps Cycles: Open Set of Tools and Methods for Lean API Development — Thomas Bush, Nordic APIs, 2019-02-14. Checked 2026-10-18. ↩︎
APIOps: End-to-End Automation Throughout the API Lifecycle — Melissa van der Hecht, Kong blog, 2021-02-24. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
How API versioning works — Stripe Docs, undated (current version 2026-09-30.endive). Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
RFC 9745: The Deprecation HTTP Response Header Field — IETF, 2025-03. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Arazzo Specification v1.1.0 — OpenAPI Initiative, 2026-05-17. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
OpenAPI Specification v3.2.1 — OpenAPI Initiative, 2026-09-10; list of versions. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Overlay Specification v1.2.0 — OpenAPI Initiative, 2026-09-22. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
Pattern: API Gateway / Backends for Frontends — Chris Richardson, microservices.io. Checked 2026-10-18. ↩︎ ↩︎
AsyncAPI Specification 3.1.0 — AsyncAPI Initiative, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
Zalando RESTful API and Event Guidelines — Zalando, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
TypeSpec and its OpenAPI3 emitter options — Microsoft, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
Spectral — Stoplight, GitHub; CLI 6.17.0 per the npm registry. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
decK file commands: openapi2kong, managing plugins, lint, add-tags (1.41 reference) — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Schemathesis and quick start — Schemathesis documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
RFC 8594: The Sunset HTTP Header Field — IETF (Informational), 2019-05. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
oasdiff documentation: breaking changes, deprecation, stability, checks; rule levels at v1.33.0. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Throttle requests to your REST APIs and API Gateway quotas — AWS documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
How Pact works and What is Pact good for? — Pact Foundation documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
oasdiff — oasdiff project, GitHub; release v1.33.0, 2026-10-01; release v1.34.0, 2026-10-18. Checked 2026-10-18 (v1.33.0), 2026-10-19 (v1.34.0 rule levels in checker/rules.go). ↩︎ ↩︎ ↩︎ ↩︎
decK release v1.68.0 (2026-09-30), root command source and environment variables in state files — Kong. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Azure/apiops-cli — Microsoft, GitHub; version 1.0.5 per the npm registry. Checked 2026-10-18. ↩︎ ↩︎
Descriptor format of catalog entities — Backstage. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
API Gateway dimensions and metrics — AWS documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
AIP-180: Backwards compatibility — Google, last changelog entry 2025-10-21. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
API Versions — GitHub Docs, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
azure-api-style-guide (archived) — Microsoft, GitHub. Checked 2026-10-18. ↩︎
RFC 9457: Problem Details for HTTP APIs — IETF, 2023-07. Checked 2026-10-18. ↩︎ ↩︎
Spectral documentation on GitHub: CLI, rules, extends, core functions — Stoplight. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
AIP-185: API Versioning — Google, updated 2024-10-22. Checked 2026-10-18. ↩︎ ↩︎
Schema Design — graphql.org, undated;
@deprecated— GraphQL Specification, September 2025. Checked 2026-10-18. ↩︎ ↩︎ ↩︎Graph API: Versioning — Meta for Developers, undated (latest v26.0). Checked 2026-10-18. ↩︎ ↩︎ ↩︎
API4:2023 Unrestricted Resource Consumption — OWASP, 2023. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
Rate Limiting plugin and configuration reference — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
RFC 6585: Additional HTTP Status Codes — IETF, 2012-04. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
RateLimit header fields for HTTP (draft-ietf-httpapi-ratelimit-headers-11) — IETF Internet-Draft, 2026-05-23. Checked 2026-10-18. ↩︎ ↩︎
rate-limit-by-key policy — Microsoft Learn, updated 2026-06-26. Checked 2026-10-18. ↩︎
Usage plans and API keys for REST APIs — AWS documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
RFC 9068: JWT Profile for OAuth 2.0 Access Tokens — IETF, 2021-10. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
RFC 9700: Best Current Practice for OAuth 2.0 Security — IETF (BCP 240), 2025-01. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎
OpenID Connect plugin (Enterprise), its reference, and Key Auth plugin — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎
deck gateway diff — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
OWASP Top 10 API Security Risks – 2023 — OWASP API Security Project, 2023. Checked 2026-10-18. ↩︎
AsyncAPI CLI usage (
diff) — AsyncAPI Initiative; version 6.2.1 per the npm registry. Checked 2026-10-18. ↩︎ ↩︎Prism README and request validation guide — Stoplight, GitHub. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
DB-less mode — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎
Tyk Operator — Tyk documentation. Checked 2026-10-18. ↩︎
jsonpath-plus — s3u, GitHub; the README documents the
~property-name selector and the@propertyfilter shorthand (version 11.2.1 per the npm registry). Checked 2026-10-19. ↩︎GitHub: workflow concurrency, deployments and environments, Node 20 deprecation (announces Node 20’s removal from runners on 2026-09-23). Checked 2026-10-18, re-opened 2026-10-19. ↩︎ ↩︎
Install Kong Gateway with Docker and supported third-party dependencies — Kong documentation. Checked 2026-10-18. ↩︎ ↩︎
Implementing SLOs — The Site Reliability Workbook, Google, online edition, undated. Checked 2026-10-18. ↩︎ ↩︎ ↩︎
Upgrade your integration — Stripe Docs, undated. Checked 2026-10-18. ↩︎
The Most Important API Metric Is Time to First Call — Postman blog, 2021-07-22. Checked 2026-10-18. ↩︎ ↩︎
Service Level Objectives — Site Reliability Engineering, Google, chapter 4, online edition. Checked 2026-10-18. ↩︎
Gateway API — Kubernetes SIG Network, undated. Checked 2026-10-18. ↩︎