APIs
Componecat exposes two programmatic surfaces — a REST API for scripts and
integrations, and an MCP server for AI agents. They share the same
authorization model, so a token’s access means the same thing on either one. The
examples below use the hosted host, https://app.componecat.ai; substitute your
own instance’s host if you self-host.
The REST API at /api/rest/v1 is the programmatic surface for CI/CD pipelines,
scripts, and external systems. It’s authenticated with a personal access token and
covers the core catalog operations — browsing, search, and the mutations behind
everything you can create or change.
https://app.componecat.ai/api/rest/v1Catalog search takes the same query language
the UI does — pass the expression as q to GET /catalog/search:
/api/rest/v1/catalog/search?q=lifecycle:production AND NOT tag:deprecatedEach search result carries the entity’s custom field values as fields, in the
same shape an entity’s detail returns, so an export can page through search
instead of fetching every entity one by one.
A semantic clause works here too —
q=kind:component ~"issues customer refunds" — and each result then carries its
relevance, the similarity to the clause text. For an organization without
search by meaning (no embedding model, or a plan that doesn’t include it) the request fails with 400 and the problem’s code is
SemanticSearchUnavailable; if the model is set up but fails or times out,
the request fails with 503 and the code SemanticSearchFailed — safe to retry.
An entity’s current scorecard results — score, attained level, and a per-rule pass/fail breakdown — are available alongside its other sub-resources:
GET /api/rest/v1/catalog/{id}/scorecardsScorecards themselves — the standards, not just the results — are a resource too: list them, read one’s definition (scope, rules, weights), or pull its rollup report (distribution, breakdown by team/kind/ladder level, per-rule failure counts, and the score trend, matching the app’s Report tab):
GET /api/rest/v1/scorecardsGET /api/rest/v1/scorecards/{id}GET /api/rest/v1/scorecards/{id}/reportThis surface is read-only, the same as the MCP scorecard tools — defining a standard is done from the organization-settings UI.
An entity’s impact analysis — its blast radius across your dependency relationship kinds — is another sub-resource:
GET /api/rest/v1/catalog/{id}/impact-analysis?direction=upstream&kindKeys=depends-on,calls&maxDepth=5All parameters are optional. kindKeys (comma-separated or repeated) names the
relationship kinds to follow; without it, every kind marked as a dependency kind
is followed, and the request returns 422 with the code NO_DEPENDENCY_KIND if
there is none. direction is downstream (the default), upstream or both,
and maxDepth is 1–50 (default 10). Each entity in the response carries its
depth, the kindKeys it was reached by, and the relationshipIds reaching it.
The older kindKey parameter still works and is added to kindKeys.
The relationship graph around an entity, and the paths connecting two entities, are sub-resources too:
GET /api/rest/v1/catalog/{id}/graph?kinds=depends-on,calls&direction=both&maxDepth=3&filter=lifecycle:productionGET /api/rest/v1/catalog/{id}/paths/{targetId}?kinds=depends-on&direction=outbound&maxDepth=6&maxPaths=3They return the same data as the GraphQL traverseGraph and findPaths queries
described below. kinds (comma-separated or repeated) names the relationship
kinds to follow — omit it to follow every kind — and direction is outbound
(the default), inbound or both. For /graph, maxDepth is 1–50 (default 5)
and filter is a query language
expression; the response lists nodes (each with its depth, the root first),
edges, and truncated. For /paths, maxDepth is 1–20 (default 10) and
maxPaths 1–20 (default 5); the response lists paths, shortest first, each
with its length, nodes and edges. Every edge carries the relationship’s
relationshipId, relationshipKindKey, its stored sourceEntityId and targetEntityId,
description, source (how it was asserted), confidence, provenanceRef,
assertedAt, lastObservedAt and attributes.
These three endpoints answer bad input with a problem-details body: 400 for
an invalid direction or a maxDepth / maxPaths outside its range, 422
for an unknown relationship kind, an invalid filter, or a path whose two ends
are the same entity, and 404 for an entity that doesn’t exist.
The wiki is available under /wiki, gated by the wiki
access category: list pages (filter by title text with q, or by a reference such
as @platform or $kind:service with ref), read, create, update and delete a
page by its slug, list a page’s references, and find the pages that reference a
catalog object:
GET /api/rest/v1/wiki/pages?q=on-call&ref=@platformPOST /api/rest/v1/wiki/pagesGET /api/rest/v1/wiki/pages/conventions/service-tiersPATCH /api/rest/v1/wiki/pages/conventions/service-tiersDELETE /api/rest/v1/wiki/pages/conventions/service-tiersGET /api/rest/v1/wiki/pages/conventions%2Fservice-tiers/referencesGET /api/rest/v1/wiki/referencing?targetType=TEAM&targetId={id}GET /api/rest/v1/wiki/sourcePOST /api/rest/v1/wiki/source/syncA page’s tags are a list of references, each exactly one reference such as
$kind:service or @platform; a create or update with any other tag returns
422 with the code WIKI_TAG_INVALID. Deleting a page moves it to Trash.
To avoid overwriting someone else’s edit, send the page’s updatedAt from the
read your change is based on as expectedUpdatedAt in the PATCH body (or the
updateWikiPage input over GraphQL). If the page has changed since, nothing is
saved and the request returns 409 with the code WIKI_PAGE_CONFLICT and the
page’s currentUpdatedAt; read the page again and reapply the change (over
GraphQL, wikiPageById(id) finds the page even if it was renamed meanwhile). Without
expectedUpdatedAt the update applies to the current version. Either way, an
update or delete that races another save of the same page returns the same
409 rather than overwriting it; repeat the request.
A slug’s / can be sent plain or encoded as %2F. Encode it on the
/references route, and when reading a page whose slug ends in /references
(/wiki/pages/api%2Freferences): sent plain, that path lists the references of
page api instead.
While the wiki is sourced from a git repository it is read-only: page writes
return 409 with the code WIKI_EXTERNALLY_MANAGED, and the pages should be
edited in the repository instead. POST /wiki/source/sync queues a sync from the
repository and returns 202; read GET /wiki/source for the outcome. When a
sync is already running nothing new is queued, and the response’s status is
already_running instead of queued. It returns
404 when the wiki has no git source, and 409 with the code WIKI_SYNC_PAUSED
while Documentation Syncing is turned off on the wiki’s integration.
The full endpoint reference is the OpenAPI document, published for tooling and SDK generation and readable without a token. It lists every endpoint and describes every request and response field, the search filters, and the shape of custom field values:
https://app.componecat.ai/api/rest/v1/openapi.jsonTeams carry externalId and externalSource when they’re synced from another
system — for an Atlassian team, externalId is its Atlassian team id — so a
script can map a Componecat team to its counterpart. See
Team sync.
POST /teams/{id}/reassign-entities moves catalog entities from the team in the
path to toTeamId: every entity it owns, or only those in entityIds, up to
5,000 per call. Only organization admins can call it, and the key needs write
access to both teams and entities. It’s all-or-nothing: if any listed entity isn’t
owned by the team, nothing moves and the response is 422. The response counts
the moved entities and, in descriptorManagedCount, those defined in a
componecat.yaml whose owner: may set the owner back on the next scan.
GraphQL
Section titled “GraphQL”The web app itself runs on the GraphQL API at /api/graphql, and it carries a few
reads the other surfaces don’t yet have. The main ones are graph traversal
and path finding:
traverseGraph returns the subgraph reachable from one entity — every entity
reached, with its hop count from the start, and every relationship followed — in a
single request, instead of a chain of one-hop lookups.
query { traverseGraph( root: { ref: "component://payments/checkout-api" } kinds: ["depends-on", "provides-api-for"] direction: OUTBOUND maxDepth: 3 filter: "lifecycle:production" ) { truncated nodes { depth entity { id displayName ref } } edges { source { id } target { id } kind { key } relationship { id description source confidence } } }}| Argument | Meaning |
|---|---|
root |
The starting entity, as exactly one of id or ref (kind://namespace/name). |
kinds |
Relationship kind keys to follow. Omit it to follow every kind your organization defines; an unknown key is an error. |
direction |
OUTBOUND follows relationships from source to target (what the entity depends on), INBOUND from target to source (what depends on it), BOTH either way. Defaults to OUTBOUND. |
maxDepth |
Hops from the root, 1–50. Defaults to 5; values outside the range are clamped. |
filter |
A query language expression. Only entities matching it are returned or traversed through, so anything reachable only through a non-matching entity is left out. The root is always returned. |
Each node’s depth is its shortest hop count from the root (the root is 0).
Each edge carries the stored relationship, so its description, source, and
confidence come with it. Deleted entities are skipped, and traversal never crosses
into another organization. A result holds at most 2,000 entities and 10,000
relationships; when a larger graph hits either cap the walk stops at that depth,
the response holds what was reached so far, and truncated is true, rather
than failing. Lower maxDepth or name fewer kinds to fit; a filter narrows
which entities are returned and expanded but does not reduce the relationships
examined against the edge cap, so truncated can be true with fewer than
10,000 relationships in the response.
subgraph returns the graph of a set of entities you already have — a page of
search results, a collection’s items — rather than a neighborhood: the entities
you name, and every relationship whose source and target are both among them. It
is what the catalog and collection graph views draw. Leave entityIds out and it
returns the graph of the whole catalog — every entity in your organization, in
display-name order — which is what the Graph page
draws.
query { subgraph( entityIds: ["<uuid>", "<uuid>", "<uuid>"] kinds: ["depends-on"] ) { truncated nodes { entity { id displayName ref } } edges { source { id } target { id } kind { key } relationship { id } } }}| Argument | Meaning |
|---|---|
entityIds |
The entities to include. Nodes come back in this order, each with depth 0; duplicates are returned once, and an id that is deleted or not in your organization is left out rather than reported. Omit it for every entity in your organization, ordered by display name. |
kinds |
Relationship kind keys whose relationships to include. Omit it to include every kind; an unknown key is an error. Naming kinds narrows the edges, never the nodes. |
The caps are the same as traverseGraph’s: at most 2,000 entities (ids past the
first 2,000 are dropped; without entityIds, the catalog is cut after the first
2,000 in display order) and 10,000 relationships, with truncated set to true
when either is hit.
findPaths answers how two entities are connected: the paths from one entity to
another across the relationship kinds you choose, shortest first.
query { findPaths( from: { ref: "component://payments/checkout-api" } to: { ref: "component://payments/ledger-db" } kinds: ["depends-on", "provides-api-for"] direction: OUTBOUND maxDepth: 6 maxPaths: 3 ) { length nodes { displayName ref } edges { source { ref } target { ref } kind { key } relationship { description source confidence } } }}| Argument | Meaning |
|---|---|
from, to |
The two entities, each as exactly one of id or ref. They must be different entities. |
kinds |
Relationship kind keys a path may follow. Omit it to allow every kind your organization defines; an unknown key is an error. |
direction |
Which way each hop follows its relationship, from from toward to: OUTBOUND from source to target, INBOUND from target to source, BOTH either way. Defaults to OUTBOUND. |
maxDepth |
Longest path to return, in hops, 1–20. Defaults to 10; values outside the range are clamped, and longer paths are never returned. |
maxPaths |
Most paths to return, 1–20. Defaults to 5; values outside the range are clamped. |
Each path lists its edges in order from from to to, and the nodes along it,
from first. Paths are simple — no entity appears twice in one — and two paths
that differ in any hop, even only in a relationship’s kind, are distinct. The
shortest path comes first; paths of the same length are compared hop by hop —
first by the relationship kind’s display order (then its key, when two kinds
share a display order), then by the name of the entity the hop reaches, compared
case-sensitively. Each path’s type is TraversalPath. Each edge keeps its stored source and target, so with INBOUND or
BOTH a hop can run from an edge’s target to its source. When the entities
aren’t connected within maxDepth the result is an empty list, not an error.
Paths never pass through a deleted entity or cross into another organization. In
an extremely dense graph the search works within the same size caps as
traverseGraph and returns the paths it found within them, so fewer paths than
maxPaths may come back and the first path returned may not be the shortest
that exists; lower maxDepth or name fewer kinds to narrow it.
impactAnalysis is the GraphQL form of
impact analysis: an entity’s blast
radius across your dependency relationship kinds.
query { impactAnalysis( entity: { ref: "component://payments/ledger-db" } direction: UPSTREAM maxDepth: 4 ) { kinds { key } truncated entities { depth entity { displayName ref } kinds { key } reachedVia { source { ref } target { ref } relationship { id } } } }}| Argument | Meaning |
|---|---|
entity |
The entity to analyze, as exactly one of id or ref. |
kindKeys |
Relationship kind keys to follow. Omit it to follow every kind your organization marks as a dependency kind; with none marked the query fails with the code NO_DEPENDENCY_KIND. An unknown key is an error. |
kindKey |
Deprecated: a single kind key, added to kindKeys. |
direction |
DOWNSTREAM follows relationships from source to target, UPSTREAM from target to source, BOTH either way. Defaults to DOWNSTREAM. |
maxDepth |
Hops from the entity, 1–50. Defaults to 10; values outside the range are clamped. |
entities lists every entity reached, nearest first, without the analyzed entity
itself. Each one’s depth is its shortest hop count; its kinds are the
relationship kinds that reach it at that distance, in display order, and
reachedVia holds those relationships. The result’s kinds lists the kinds that
were followed. Impact analysis runs on the same traversal as traverseGraph, so
the same scoping and the same size caps apply, and truncated means the same.
similarEntities is “more like this”: the entities whose meaning is closest to
the one you name, nearest first, never including the entity itself. It needs
search by meaning for the organization — a plan that includes it and an embedding model; see Turning on search by meaning.
query { similarEntities(entityId: "8a7c3f4e-1d2b-4c5a-9e6f-0b1c2d3e4f5a", limit: 5) { relevance entity { displayName ref kind { key } } }}| Argument | Meaning |
|---|---|
entityId |
The entity to find neighbors for. An unknown or deleted entity fails with NOT_FOUND. |
limit |
How many entities to return. Defaults to 10, and never more than your instance’s semantic result limit. |
relevance is the cosine similarity to the entity (−1 to 1, higher is closer), and
only entities at or above the instance’s similarity threshold come back. An entity
that was created moments ago may return an empty list until background indexing
has embedded it. For an organization without search by meaning the query fails with the code
SEMANTIC_SEARCH_UNAVAILABLE; ask semanticSearchAvailable (a Boolean!) first to
know whether the caller’s organization has it. searchCatalog items also carry relevance when
the query has a semantic clause; when the embedding model fails or times out while
reading the clause, searchCatalog returns a queryError with code
SEMANTIC_SEARCH_FAILED.
relevantDocumentation finds the documentation sections whose meaning is closest
to a question, nearest first, each with the entity it documents. It needs
search by meaning too.
query { relevantDocumentation(text: "how do we rotate the payment gateway key", limit: 5) { relevance snippet section { title sectionType } entity { displayName ref } }}| Argument | Meaning |
|---|---|
text |
The question or topic, in plain language. |
entityId |
Optional. Search only this entity’s documentation. An unknown or deleted entity fails with NOT_FOUND. |
limit |
How many sections to return. Defaults to 10, at most 50, and never more than your instance’s semantic result limit. |
relevance is the cosine similarity between the text and the section, and only
sections at or above the instance’s similarity threshold come back. snippet is the
passage around the text when the section contains it word for word, otherwise the
passage around the longest word of the text (four or more characters) that the
section contains, and otherwise the section’s opening. A section edited moments ago may be missing until background
indexing has embedded it. For an organization with no embedding model the query fails with
SEMANTIC_SEARCH_UNAVAILABLE; when the embedding model fails or times out, it fails
with SEMANTIC_SEARCH_FAILED.
The Model Context Protocol server at /api/mcp is the surface built for AI
agents — a tool catalog plus a browsable entity resource tree, with OAuth device
flow authorization. Its search_catalog tool takes the same
query language as the UI and the REST
API, so an agent can express an entire question in one call — including a
semantic clause, which lets an agent search by
what a component does rather than what it’s called. When your organization has an
embedding model, two more tools start from a plain-language description:
find_relevant_entities returns the best-matching entities, and
find_relevant_documentation returns the best-matching documentation sections.
The wiki tools —
list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, and
get_guides_for, which finds the guides for a target written in the
reference syntax — are gated by the
wiki access category. See
Connect AI agents for the full walkthrough.
https://app.componecat.ai/api/mcpAuthentication
Section titled “Authentication”Catalogs are private and organization-scoped, so every surface requires authentication.
- Personal access tokens — bearer tokens you create in your access-token settings. Used by the REST API and by non-interactive MCP clients.
- OAuth device flow — the standard flow for interactive MCP agents, with dynamic client registration and automatic authorization-server discovery.
Tokens are scoped: you grant read or write access per resource category (catalog entities, teams, schema, wiki), and can scope a token to specific entities. Grant the least access that does the job.
Related
Section titled “Related”- Outgoing webhooks — events Componecat pushes to your systems
- Wiki
- Connect AI agents
- Catalog as code