Blog

Elevate an Existing API: From Pass-Thru to Capability

Kin Lane ·April 30, 2026
Table of contents

I keep getting pulled into rooms where the proposed answer to “our API is hard to consume” is to rewrite it. Some working API that has been in production for six or eight or twelve years, owned by some team that half the meeting attendees can’t name, and the fix on the whiteboard is a new service. A new data model. A new set of endpoints. Six months of work. Minimum.

That is almost never what the situation actually calls for. The API is not the problem. The API is uncurated. The third use case in the Naftiko Framework is the one I lean on hardest in those rooms — elevate an existing API. Wrap it as a capability. Keep the API. Change the surface.

This is post three in the nine-part walk through the framework use cases.

The rewrite reflex

The rewrite reflex is a very old pattern in the API space. An existing API is inconsistent with some newer convention. It has endpoints nobody remembers shipping. It has a handful of POSTs that really should have been GETs. It returns XML on one path and JSON on another. It has seven auth schemes because seven different teams added one each between 2014 and 2022.

None of that is broken. It is just uncurated. And the industry response, for about as long as I have been writing about this, has been the same: throw it out, build a new one, tell everybody to migrate. That migration then takes the rest of the decade, because the existing consumers are real.

What changes when you have a capability spec is you can stop asking the question “how do we replace this?” and start asking “how do we wrap it?”

The pass-thru source capability

The Naftiko capability spec gives you a straightforward shape for this. You declare the existing API as a source in consumes — every operation you actually want to elevate, with its real method, real path, real parameters. Then you expose a curated set of paths outward, under a clean namespace, through a rest adapter, an mcp adapter, or both. The engine runs it. Nothing changes upstream.

Here is the shape of a pass-thru capability for a legacy inventory API:

naftiko: "1.0.0-alpha1"

info:
  title: Inventory
  description: "Elevated capability over the legacy warehouse inventory API"

capability:
  consumes:
    - namespace: warehouse-legacy
      type: http
      baseUri: "https://inventory.internal.legacy"
      authentication:
        type: basic
        username: ""
        password: ""
      resources:
        - name: stock
          path: "/ws/StockQuery"
          operations:
            - name: query-stock
              method: POST
              inputParameters:
                - name: sku
                  in: body
                  type: string
                  required: true
            - name: list-locations
              method: GET
              path: "/ws/Locations"

  exposes:
    - type: rest
      namespace: inventory
      basePath: "/api/inventory"
      endpoints:
        - method: GET
          path: "/stock/{sku}"
          description: "Get stock level for a SKU"
          call: warehouse-legacy.query-stock
          inputParameters:
            - name: sku
              in: path
              type: string
              required: true
              mapping: "body.sku"

        - method: GET
          path: "/locations"
          description: "List warehouse locations"
          call: warehouse-legacy.list-locations

    - type: mcp
      namespace: inventory
      tools:
        - name: get-stock
          description: "Look up the current stock level for a SKU"
          call: warehouse-legacy.query-stock
          inputParameters:
            - name: sku
              type: string
              required: true
              mapping: "body.sku"

Two things worth noticing in that file.

One — the existing API is doing a read-only stock query as a POST, which is the single most common legacy sin I run into. The capability exposes it as a cacheable GET. That is a tiny detail. It changes the HTTP semantics for every downstream consumer, including any CDN, any conditional-request caching layer, and any agent runtime that is quietly batching calls. One line of declarative mapping replaces a months-long argument about whether to “fix it in the source.”

Two — the exposed namespace is inventory. Not warehouse-legacy. The consumer never sees the upstream name. The capability is named after the domain it serves, not the system it wraps. That namespace is part of what the capability schema enforces.

Schema-based validation, not tribal knowledge

The part of this use case I think gets underweighted is the Spectral piece.

Governance in the API space has, for a long time, meant either “a PDF nobody reads” or “the gatekeeper in the architecture review board.” The capability spec changes the shape of the question. The capability is a file. The schema is a file. The rules — namespace uniqueness, path conventions, security checks — run in CI.

When I say “elevate an existing API,” I do not just mean “put a cleaner face on it.” I mean: give it a machine-checkable contract that lives in Git next to the spec itself. That is what capability-schema.json and the Spectral rules are for. A namespace collision is a lint error. A path that does not follow convention is a lint error. A missing auth block is a lint error. None of this requires a human gatekeeper.

That matters for the politics of the thing, which I will get to in a second.

Three dimensions, again

I use the same three-dimensional lens for every API topic — technology, business, politics. This use case touches all three in different ways than AI Integration did.

Technology. The elevated capability is declarative pass-thru with curated paths. The source API stays as-is. The engine handles HTTP calls, auth, body shaping, and response forwarding. If the upstream returns XML, Avro, CSV, or HTML, format-aware parsing converts it to JSON on the way out. You change the HTTP method where it should have been different all along. You expose the paths you want to expose, and nothing else.

Business. Nobody has to sell a rewrite. The existing API keeps serving its existing consumers. The new capability layer serves new consumers — including AI agents via MCP — without negotiating migration timelines with every team that depends on the old thing. The capability is a file in Git. It goes through review. It lints. It ships. The cost of extending the API’s reach drops by an order of magnitude.

Politics. This is where the rewrite reflex really lives. Rewrites are expensive because they are political, not because they are hard. Every rewrite requires someone to tell every consumer “you will migrate.” Capabilities skip that fight. The platform team owns the capability layer. The legacy owners keep owning the upstream. Nobody has to agree on a sunset date. The surface area for disagreement shrinks to the capability file, which is a diff anyone can read.

That last bit is what makes this pattern stick in organizations that have tried the rewrite path and failed.

Features that matter here

Pulling from the wiki, the features that carry this use case:

  • Pass-thru source capability with one or many HTTP adapters
  • Declarative source HTTP adapter and target REST adapter
  • Focus on source APIs with JSON payloads — with format-aware parsing for XML, Avro, CSV, TSV, PSV, HTML, Markdown
  • Change HTTP methods to expose proper semantics — read-only POST queries become cacheable GETs
  • Schema-based validation with Spectral rules — namespace uniqueness, path conventions, security checks
  • Forward proxy for selective pass-through routing — trusted header forwarding for the routes you haven’t curated yet

The forward proxy detail is the one that lets this pattern work in messy environments. You do not have to curate every path on day one. You curate the ones that matter, you let the rest pass through with header forwarding, and you keep iterating.

Where this is showing up right now

Most of the partner capabilities I have been writing lately fall into this shape. An existing vendor API. A few paths the consumer actually cares about. A capability file that takes the vendor’s schema and re-exposes it under a cleaner namespace with saner HTTP semantics. The MCP surface and the REST surface come from the same file.

The conversation in the room changes when the artifact is a capability instead of a proposed rewrite. Instead of “here is our estimate for rebuilding the inventory API,” it is “here is the capability file — which paths should we curate first?”

That is a better conversation to be in.

Next

Next post in the series is Elevate Google Sheets API — the same pattern applied to something most organizations don’t think of as an API at all. A spreadsheet. Same three-dimensional lens.

I will keep walking the list.