How it works

Modeling “who calls this endpoint” as a graph

Last checked 2026-11-1412 min read

An API consumer graph is a record of which teams call which endpoints, and which team owns them. It needs three kinds of node (team, service, endpoint) and three kinds of edge (a team owns a service or an endpoint, a service exposes an endpoint, a team subscribes to an endpoint). The consumer edges are declared by the consumer teams themselves, because they are the only ones who know what they call. With that in place, “who does this change affect?”, “what does my team depend on?” and “which endpoints have no owner or no consumer?” are queries of one or two hops. Graphwake keeps this graph, routes each breaking change along its subscription edges, and fills gaps with rule-based Discovery suggestions that a person confirms.

ExampleService graph · orders-api
GET/orders/{id}2 consumer teams
Service
orders-api
Owner
payments-team

Consumers

  • checkout · 4 peopleSlack #team-checkout
  • mobile-app · 6 peopleSlack #mobile-alerts

Suggested by Discovery

  • storefront · uses a neighbouring endpointmaybe · 72
GET/orders/{id}/invoiceBlind spot

No team subscribes to this endpoint. A breaking change here reaches no subscribed team.

Who owns an endpoint, who consumes it, and where nobody is listening. A suggested team is added only when someone confirms it.
On this page
  1. The question behind every API change
  2. Three nodes, three edges
  3. A sketch of the model
  4. Why the consumers keep the list
  5. Three questions the graph answers
  6. Filling the gaps: Discovery
  7. What the model leaves out
  8. How Graphwake does it
  9. Where to go next
  10. Questions

The question behind every API change

Before a team changes an endpoint, someone has to answer a plain question: who calls this? In most companies the answer lives in people’s heads, in an old wiki page, or in a Slack thread that starts after something broke. What you are really asking about is a relationship between teams and endpoints, so we store it as one, in a graph.

Three nodes, three edges

NodeWhat it isWhere it comes from
ServiceOne API with its own OpenAPI spec, such as orders-api.Set up in Graphwake with its spec, from a published URL or a CI step.
EndpointOne method and path of a service, such as GET /orders/{id}.Read from the spec. A removed endpoint stays on record for its history.
TeamA group of people with one place for alerts: a Slack channel, a Microsoft Teams channel or an email address.Created in Graphwake by the people who run it.
EdgeFrom → toKept byRule
exposesService → EndpointThe specEvery endpoint belongs to exactly one service.
ownsTeam → Service, or Team → EndpointAn adminOptional and single-team. An endpoint with its own owner overrides its service’s owner; otherwise it inherits it.
subscribes toTeam → EndpointThe consumer teamPer whole endpoint. A team subscribes to each endpoint it calls.
The consumer graphA team owns a service. The service exposes endpoints. A team subscribes to an endpoint. An endpoint with no subscriber is marked as an orphan.TeamownsServiceexposesexposesEndpointhas a subscriberEndpointorphan: no subscribersubscribes tonobody subscribes
The consumer graph: a team owns a service, the service exposes endpoints, teams subscribe to the endpoints they call. An endpoint with no subscriber is an orphan.

The visual at the top of this page is the same model drawn for one Example service. orders-api is owned by payments-team. Its endpoint GET /orders/{id} has two subscribed consumer teams, checkout and mobile-app, and one team Discovery suggests, storefront. GET /orders/{id}/invoice has no subscriber at all: an orphan, shown as a blind spot. The rest of this page uses that example.

A sketch of the model

The graph is small and shallow, so four relational tables hold it comfortably. The sketch below is a generic example of the shape, not Graphwake’s schema:

text
-- Example: a minimal relational sketch of the model. Not Graphwake's schema.
CREATE TABLE teams (
  id    uuid PRIMARY KEY,
  name  text NOT NULL UNIQUE
);

CREATE TABLE services (
  id             uuid PRIMARY KEY,
  name           text NOT NULL UNIQUE,
  owner_team_id  uuid REFERENCES teams (id)          -- optional: owns
);

CREATE TABLE endpoints (
  id             uuid PRIMARY KEY,
  service_id     uuid NOT NULL REFERENCES services (id),  -- exposes
  method         text NOT NULL,
  path           text NOT NULL,
  owner_team_id  uuid REFERENCES teams (id),         -- optional override
  removed_at     timestamptz,                        -- kept for history
  UNIQUE (service_id, method, path)
);

CREATE TABLE subscriptions (                         -- subscribes to
  team_id      uuid NOT NULL REFERENCES teams (id),
  endpoint_id  uuid NOT NULL REFERENCES endpoints (id),
  created_at   timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (team_id, endpoint_id)
);
Example: the consumer graph as tables. Each edge is a column or a row.
  • Ownership is a nullable column rather than a table. One team answers for a service, and an endpoint may name a different one. It may stay empty, because an unowned endpoint you can see is more useful than a wrong name.
  • A subscription is a row in a table with a composite key, so a team subscribes to an endpoint once. There is no field column, because a subscription covers the whole endpoint.
  • Endpoints are not deleted. When an endpoint disappears from the spec it is marked removed. The live graph hides it, and the change history still points at it.
  • Edges have history too. Graphwake records each subscribe and unsubscribe, each ownership change, and each published change with the teams it reached. So it can later show that checkout heard about a change because it subscribed to GET /orders/{id} three months earlier.

Why the consumers keep the list

The producer team looks like the natural owner of a consumer list, but it is in a poor position to keep one. The producer sees requests arrive, but not which team’s code sent them or what that code relies on. Each consumer team knows exactly which endpoints it calls, and it learns this at a precise moment: when it writes the integration. Recording the edge at that moment takes a minute. Reconstructing it later from the producer’s side takes a search across every repository, and that search still misses some callers.

  • A subscription is a consumer team saying “we call this”, so the side that has the knowledge declares the edge and nobody has to infer it.
  • The list stays current because we route every breaking change along it. A team that missed an alert finds out why and subscribes. A team that keeps getting alerts it does not need removes the edge.
  • A team that is not subscribed is not told, and the producer can see that this was a decision rather than an oversight. In a broadcast channel there is no way to tell a team that is not affected from one that missed the post.
  • An endpoint with no subscriber shows up as an orphan, with a count next to it, so the gap is on screen rather than in someone’s head.

Traffic logs and code search help you find callers you do not know about yet, and API consumer tracking compares those methods. They tell you which client called recently, or which repository contains a URL. A subscription tells you which team wants to hear when the contract changes, and that is what you need to know when a change breaks it.

Three questions the graph answers

Who does this change affect?

Start from the endpoints a change set touches and follow the subscription edges back to teams. That is one hop.

text
-- Example: which teams does a change to these endpoints reach?
SELECT DISTINCT t.name
FROM subscriptions s
JOIN teams t ON t.id = s.team_id
WHERE s.endpoint_id = ANY ($1);   -- the endpoints with a breaking change
Example: the teams a change reaches.

In the example, a breaking change to GET /orders/{id} reaches checkout and mobile-app, and no other team. A breaking change to GET /orders/{id}/invoice reaches nobody, and that is the result to worry about. Graphwake counts it as a blind spot in the change’s impact score, so the gap shows while the change is still a pull request. Anatomy of a breaking change follows one such change from spec to alert.

What does my team depend on?

Start from a team, follow its subscription edges to endpoints, then each endpoint’s owner edge (or its service’s) to the team to ask. That is two hops.

text
-- Example: what does one team depend on, and whom does it ask about each endpoint?
SELECT sv.name AS service, e.method, e.path,
       ot.name AS owner            -- the endpoint's owner, else the service's
FROM subscriptions s
JOIN endpoints e   ON e.id = s.endpoint_id AND e.removed_at IS NULL
JOIN services sv   ON sv.id = e.service_id
LEFT JOIN teams ot ON ot.id = COALESCE(e.owner_team_id, sv.owner_team_id)
WHERE s.team_id = $1
ORDER BY sv.name, e.path, e.method;
Example: one team’s dependencies, with the team that answers for each.

For checkout, that is GET /orders/{id} in orders-api, owned by payments-team, plus whatever else checkout calls. A team wants this list in front of it when it plans an upgrade or runs an incident review. Your AI assistant can read the same list through Graphwake MCP.

Which endpoints have no owner or no consumer?

Look for endpoints with a missing edge: no subscription, or no owner on either the endpoint or its service.

text
-- Example: live endpoints with no consumer (orphans) or no owner
SELECT sv.name AS service, e.method, e.path,
       NOT EXISTS (SELECT 1 FROM subscriptions s WHERE s.endpoint_id = e.id) AS orphan,
       COALESCE(e.owner_team_id, sv.owner_team_id) IS NULL                  AS unowned
FROM endpoints e
JOIN services sv ON sv.id = e.service_id
WHERE e.removed_at IS NULL
  AND (NOT EXISTS (SELECT 1 FROM subscriptions s WHERE s.endpoint_id = e.id)
       OR COALESCE(e.owner_team_id, sv.owner_team_id) IS NULL);
Example: the two kinds of gap in one query.

An orphan is either unused or unmapped, and you cannot tell which from the graph alone. An unowned endpoint has nobody to ask. Graphwake flags orphans on the service graph and, when the weekly report is turned on, emails admins a weekly list of orphan endpoints, endpoints without an owner and services without an owner. Orphaned API endpoints has a routine for clearing them.

Filling the gaps: Discovery

A declared graph is only as complete as its declarations. New endpoints and forgotten subscriptions need a nudge. Discovery is rule-based: after every spec arrives, it looks at each new endpoint and each endpoint with a breaking change, and asks which teams that are not subscribed may be affected. It uses only data the graph already holds, and it shows every signal it found as the reason for the suggestion.

SignalWhat it readsIn the example
Subscriptions nearbyThe team subscribes to another endpoint of the same service under the same path prefix.storefront subscribes to GET /orders, next to GET /orders/{id}.
Shared schemaThe team subscribes to an endpoint whose request body or success response uses the same named schema.storefront also subscribes to POST /orders, which returns the same Order schema as GET /orders/{id}.
OwnershipThe team owns the service or the endpoint.payments-team, for a new endpoint in orders-api (on its own, too weak to be listed).
Maybe movedThe change set removed an endpoint and added another that may replace it; the old endpoint’s subscribers are suggested for the new one.If GET /orders/{id} were replaced by GET /v2/orders/{id}, checkout and mobile-app would be suggested for the new path.
Past decisionsThe team said “Not us” to another endpoint with the same schema. This lowers the score.A team that already declined an Order endpoint is less likely to be suggested for the next one.

Each signal adds a fixed weight and a past “Not us” subtracts one, with the total kept between 0 and 100. There is no language model involved. Suggestions under 40 are counted but not listed, so weak guesses stay out of people’s way. A person always decides: the suggested team (or an admin) chooses Confirm, which subscribes the team, or Not us. A decision is final for that team and endpoint, and a subscribed team is never suggested. When a released change breaks an endpoint and a suggestion for it scores 60 or more, the suggested team gets one “This may affect you” note with Confirm and Not us links; a workspace can turn this off.

One weak signal is not enough. storefront is listed because two signals agree: a nearby subscription and a shared schema. If the orphan GET /orders/{id}/invoice changed in a breaking way, the path rule would point at checkout and mobile-app, because both subscribe to GET /orders/{id} just above it. On its own that rule scores under 40, so those suggestions are counted but not shown, and the orphan stays a visible blind spot until a team subscribes.

What the model leaves out

Each of these omissions is deliberate, and each one keeps the graph small enough for people to maintain:

  • There are no field-level subscriptions. A team subscribes to a whole endpoint. A removed field reaches every subscriber of that endpoint, including a team that never read it. The cost is an occasional alert a team marks as handled. In return, the list stays small enough for a person to keep accurate.
  • There is no traffic or code analysis. Graphwake reads only the OpenAPI spec. It does not read source code or request logs, so an edge exists because a team declared it or confirmed a suggestion.
  • Consumers are teams, not services. The edge runs from a team to an endpoint. A breaking change is a conversation between people, so the graph records who to talk to, not which process opens the connection.
  • Each node has one owner. When two teams share ownership, questions tend to go unanswered. The model allows one team per service and one override per endpoint.
  • Follows are not edges. Anyone can follow an endpoint and get its alerts by email, but a follow is not a subscription: it never appears in the graph, the consumer list, the impact score or Discovery.

How Graphwake does it

  • Subscriptions per endpoint

    Consumer teams subscribe to the endpoints they call. A breaking change goes to those teams in Slack, Microsoft Teams or email, not to a company-wide channel.

  • Optional ownership with inheritance

    A service may have an owner team, and an endpoint may override it. Orphans are flagged on the graph, and when the weekly report is turned on, admins get a weekly email listing orphans and unowned endpoints and services.

  • Rule-based Discovery

    Nearby subscriptions, shared schemas, ownership, “maybe moved” matches and past decisions suggest teams, each with its reason. A person confirms before a team subscribes.

  • Edges with history

    Subscriptions, ownership changes and the teams each change reached are kept in an append-only change history, so an alert can always be traced to the subscription behind it.

See who your next breaking change reaches.Paste a spec URL or add one CI step. 7-day free trial. Cancel before day 7 and you pay nothing.
Start free trial

Where to go next

For the wider picture of who-calls-what maps, including traffic-based ones, read API dependency map. To find the teams behind an endpoint before they are on the graph, see how to find which teams use an API endpoint.

Questions

Do I need a graph database to model API consumers?

Usually not. The useful questions are one or two hops: from a changed endpoint to its subscribed teams, from a team to its endpoints and their owners. A few relational tables with the right keys answer them. A graph database starts to pay off when you need long, variable-length paths, which a team-to-endpoint map rarely does.

Why are the consumers teams and not services?

Because the reason to know a consumer is to tell someone about a change. A team has members and a channel; a service does not read Slack. A team that runs several services subscribes once per endpoint it calls.

How does Discovery decide which teams to suggest?

Discovery is rule-based. It scores each team from signals already in Graphwake: subscriptions to nearby endpoints, a shared request or response schema, ownership, a “maybe moved” match when an endpoint is replaced, and past “Not us” decisions. Each suggestion shows its reasons, and a team becomes a subscriber only when someone confirms it.

Can a team subscribe to a single field?

No. A subscription covers the whole endpoint. A breaking change to any part of it reaches every subscribed team, and a team that is not affected can mark the change as handled.

Does following an endpoint make me a consumer?

No. A follow sends you that endpoint’s alerts by email, but a follow is not a subscription. It does not appear in the graph, the consumer list or the impact score.

Pick an endpoint. See who it reaches. 7-day free trial. Cancel before day 7 and you pay nothing.

Start free trial