Map the API
Keep an API inventory
Track deployed hosts, routes, methods, versions, owners, data classes, and retirement dates so forgotten endpoints do not remain exposed.
You cannot protect an endpoint nobody remembers. Old API versions often keep old weaknesses alive, and attackers hunt for them because they know teams forget.
Build the inventory from more than one source
Documentation drifts. Build the inventory from source, gateway, deployment, and observed traffic, then compare them against each other.
# routes the code declares
grep -rE "router\.(get|post|put|patch|delete)" src/routes/ | sort -u
# routes production actually serves
awk '{print $6, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Observed traffic is stronger evidence than documentation alone. A mobile client may still call /v1/invoices months after the web app moved to /v2. Removing the route too early breaks customers, but leaving it ownerless preserves old authorization bugs.
Record ownership and lifecycle
Give each API an owner and lifecycle state. A useful inventory row looks like this:
host: api.example.com
route: GET /v1/invoices
owner: billing-team
state: deprecated
deployment: invoices-v1 (build 2026-05-02)
last-seen: 2026-07-29T14:22:10Z
retirement: 2026-10-01
Include the deployment identifier and last-seen timestamp so the owner can distinguish a live route from stale gateway configuration. Remove unused routes and credentials instead of leaving “temporary” compatibility forever.
Shadow and zombie APIs are the two failure modes this catches: routes that shipped without ever entering a review, and routes that outlived their owners. The inventory exists to make both visible before an attacker does.
Verify the inventory catches drift
An inventory earns trust when it flags a route you did not expect:
curl -i https://api.example.com/v1/debug/config
# HTTP/1.1 404 Not Found <- good, it was removed
# HTTP/1.1 200 OK <- bad: undocumented, no owner, still serving
A common failure mode: the gateway routes /v1/* to an old deployment as a catch-all, so routes deleted from the code still resolve in production. Comparing gateway rules against source routes is the only way to catch that, because neither source alone shows the mismatch.
Compare source routes, gateway routes, and access logs for one environment. Save the mismatch list, then request one undocumented route and prove it is removed or has an owner and retirement date.
Lesson completed