Need help with your APIs? I offer API discovery, governance & evangelism services. Explore services →
API Evangelist API Evangelist
Discovery
Learnings
Guidance
Toolbox
Alignment
API Evangelist LLC

OpenAPI Overlays for Separation of Concerns When You Do Not Own the Spec

July 18th, 2026 · Kin Lane
OpenAPI Overlays for Separation of Concerns When You Do Not Own the Spec

Most of the overlay conversation assumes you are the API producer, sitting on top of a spec you wrote and own. But the majority of the OpenAPI I touch in a given week belongs to somebody else. I am a consumer. I pulled their spec down, and now I want to bend it to fit my world without editing a single line of what they published. This is the use case I keep coming back to, and it is the one the overlay pattern was practically born for. It is one of the entries in my many use cases for OpenAPI overlays, and it deserves its own walk-through, because the alternative people reach for—forking the spec—is a slow-motion mistake.

Let me set the stage. Pretend the Products API is a vendor’s API. I did not write it. Its OpenAPI lives at a URL I do not control, at https://raw.githubusercontent.com/api-evangelist/products-api/main/openapi/products-api-openapi.yml. It has the operations I need—listing products, fetching one by ID—and a pile of operations I do not. The base URL in their servers block points at their environment, but I route everything through my own gateway. I want to annotate a couple of operations with notes my team needs. And I want none of that to end up in a pull request against their repo, because it never would get merged, and it is not my repo to touch anyway.

Here is my first overlay, the consumer-side changes.

overlay: 1.1.0
info:
  title: Products API - Internal Consumer Overlay
  version: 1.0.0
extends: https://raw.githubusercontent.com/api-evangelist/products-api/main/openapi/products-api-openapi.yml
actions:
  - target: $.servers
    description: Point clients at our internal gateway, not the vendor origin.
    update:
      - url: https://api.internal.example.com/products-proxy
        description: Products API behind our gateway (auth, quota, logging)
  - target: $.paths['/products'].get
    description: Note our internal caching behavior on the list operation.
    update:
      x-internal-note: Responses cached 60s at the edge. Do not use for realtime inventory.
  - target: $.paths['/products/{id}'].get
    description: Flag the fields our billing team actually depends on.
    update:
      x-internal-note: sku and price are load-bearing for invoicing. Alert on schema drift.

The extends line is the whole trick. It says my base is their spec, fetched live at their URL, and my actions are applied on top of it. The first action replaces the entire servers array with my gateway URL, so every client I generate points at my proxy instead of the vendor origin. The next two actions reach into specific operations by JSONPath and merge in x-internal-note extensions—notes for my people, invisible to the vendor, living in my repo. Nothing here modifies their file. When the vendor publishes a new version tomorrow with three new endpoints and a bug fix, I re-run the overlay and my server URL and my notes land right back on top of their fresh spec. That is the part a fork can never give you.

The second thing I always want as a consumer is a smaller surface. Their spec has operations I will never call, and every one of them becomes dead code my client generator dutifully emits. So I trim.

overlay: 1.1.0
info:
  title: Products API - Trim To Used Operations
  version: 1.0.0
extends: https://raw.githubusercontent.com/api-evangelist/products-api/main/openapi/products-api-openapi.yml
actions:
  - target: $.paths['/products'].post
    description: We never create products; remove the POST.
    remove: true
  - target: $.paths['/products/{id}'].put
    description: We never update products.
    remove: true
  - target: $.paths['/products/{id}'].delete
    description: We never delete products.
    remove: true
  - target: $.paths['/products/{id}/cancel']
    description: Cancel is out of scope for our integration.
    remove: true

Here every action uses remove: true against a JSONPath that points at an operation or a whole path. The write operations go, the cancel operation goes, and what survives is exactly the read surface my integration touches. Generate a client from the overlaid result and it is lean—no methods for things I would never call, no confusing my teammates with capabilities we do not use. And again, this is a re-appliable diff. It expresses my intent (“only these operations”), not a snapshot of their spec at a moment in time.

That distinction is the entire argument. A fork is a copy that starts identical and rots from the moment they push their next commit. Every upstream change becomes a merge conflict you resolve by hand, forever, and you are one busy week away from silently running six months behind their security fixes. An overlay is a clean, declarative statement of the handful of things you need to be different, and it survives regeneration because it is applied against whatever they publish, not welded to one version of it. Read the overlay specification and you will see it was designed exactly for the party who does not own the base.

So here is my take: if you are a consumer editing a copy of someone else’s OpenAPI, stop. You are building a fork you will have to feed for the rest of the integration’s life. Write the overlay instead, check it into your repo, and let their spec stay theirs. Your changes are your side of the contract. Keep them there.