Documentation for v2.10.0. Read the latest, v2.12.0.
Reference
REST API
API contract for tooling that talks to Sanshain (build plugins, CLIs). A Producer provides its complete specification under a producer-declared version; a Consumer pins an exact version and requires only the endpoint snippets it uses. Covers providing specifications (OpenAPI, AsyncAPI, gRPC/Proto) and requiring endpoint snippets. See CONTEXT.md for the vocabulary and docs/adr/0003-versions-replace-branches.md for the model.
The version lives in the spec file. For OpenAPI and AsyncAPI it is read from info.version; for proto from a mandatory // sanshain-version: MAJOR.MINOR.PATCH comment. Accepted spellings are MAJOR[.MINOR[.PATCH]] with an optional leading v — omitted parts are zero (v2 → 2.0.0) and the stored/answered form is always the full three-part version. No -SNAPSHOT or other suffixes.
Stability is declared on every Provide: snapshot (overwritable work-in-progress, expires when unused) or ga (immutable; the number is permanently claimed). Resolution prefers GA, falls back only to the same-numbered snapshot, and otherwise fails immediately — no fallback, no long-polling.
2.0 is a clean break of the 1.x branch-based contract: branch, base_version, force, timeout, source_protected_branch and pull_from_branch are no longer accepted anywhere and are rejected as unknown fields.
Only the canonical names producername and consumername are accepted; the 1.x aliases servicename and clientname are rejected as unknown fields like every other branch-era leftover.
Tokens, scopes, sessions and error shapes are explained in Using the API.
Endpoints#
| Method | Path | Summary |
|---|---|---|
| POST | /provide | Provide an OpenAPI specification under its declared version |
| POST | /provide/asyncapi | Provide an AsyncAPI specification under its declared version |
| POST | /provide/grpc | Provide a .proto file under its declared version |
| GET | /require | Retrieve an endpoint snippet at a pinned version |
| GET | /require/asyncapi | Retrieve an AsyncAPI channel snippet at a pinned version |
| GET | /require/grpc | Retrieve a gRPC method snippet at a pinned version |
| POST | /require-bundle | Retrieve multiple endpoint snippets as a single merged spec |
| PUT | /catalog/{systemname} | Record a system's periphery and context from its Backstage catalog-info.yaml |
| GET | /producers/{producername}/versions | List a Producer's version lines |
| GET | /report | The dependency report of a graph scope |
| GET | /report/markdown | The dependency report as Markdown |
| GET | /report/isolation | The isolation report |
| GET | /endpoint-versions | The history of one endpoint across a Producer's version line |
| GET | /api/audit/timeline | Audit entries, newest first |
| GET | /api/sse/updates | Catalog change notifications as server-sent events |
| GET | /api/ws/updates | Catalog change notifications over a WebSocket |
| POST | /validate | Validate a specification without publishing anything |
| GET | /version | Report the running Sanshain instance version |
| GET | /auth/tokens | List the caller's API tokens |
| POST | /auth/tokens | Create an API token |
| DELETE | /auth/tokens/{id} | Revoke an API token |
/provide#
POST /provide#
Provide an OpenAPI specification under its declared version
Authentication: session or API token (full)
Parses the provided OpenAPI YAML, reads the version from info.version, splits the document into individual endpoint snippets, and stores the full document and the snippets as one version-line entry for the Producer.
Releasing is a role: stability: ga — a fresh GA, a promotion, or even an idempotent GA re-provide — requires the release_ga permission (the releaser role; admins and root hold it implicitly) and is otherwise refused 403. Snapshots need only authentication.
Idempotency: re-providing byte-identical content is a no-op — CI re-runs of the same commit never fight. changes reports all zero. The GA gate still applies first: for stability: ga the caller must hold release_ga even when the re-provide would change nothing.
Snapshots are overwritable by their provider: a snapshot Provide for an existing snapshot version replaces it wholesale when it comes from the user who last provided it (the audit trail still names every overwrite). Different content from anyone else is rejected 409 (snapshot_owned) naming the provider and proposing the next free minor, so two developers who picked the same number cannot break each other's Consumer builds; CI rebuilding what its developer provided sends identical bytes and stays a no-op. An administrator deletes the snapshot to free the number.
GA is immutable: a Provide for an existing GA version with different content is rejected 409 with proposed_version — the next free number, bumped by what actually changed (breaking → major, additive → minor, shape-identical → patch). A GA Provide for a version that exists as a snapshot promotes it in place. A snapshot Provide for a version that has gone GA is rejected — a released number can never carry a snapshot again.
Semver honesty: a GA Provide whose changes relative to the highest GA below it are breaking without a major bump is rejected 409 with the correct proposed_version. Snapshots are never compatibility-checked.
Request body
application/json
Schema: ProvidePayload.
Example request body (recorded from a live instance)
{
"producername": "payments",
"stability": "snapshot",
"openapi_yaml": "openapi: 3.0.3\ninfo:\n title: Payments\n version: 1.0.0\npaths:\n /payments:\n post:\n summary: Create a payment\n responses:\n '201':\n description: Created\n",
"trunk": true
}
Responses
| Status | Description | Body |
|---|---|---|
202 | Specification accepted and stored — or, for Header | one of ProvideResponse, RetiredProtocolResponse
Example 202 response (recorded from a live instance) |
400 | Invalid request — malformed spec YAML, a missing/non-semver | ErrorBody |
403 |
Also Also any request with a | ErrorBody |
409 | Rejected by the version rules. The body carries | ErrorBody
Example 409 response (recorded from a live instance) |
413 | Request body exceeds the configured size limit ( | — |
422 | Request body has the wrong shape — missing | — |
/provide/asyncapi#
POST /provide/asyncapi#
Provide an AsyncAPI specification under its declared version
Authentication: session or API token (full)
Same rules as /provide, for AsyncAPI 2.x/3.x documents (version auto-detected). The version is read from info.version. Only PUB (publish/send) operations are stored; SUB channels belong in a Consumer's requires. On GA provides, message-level channel contracts are reconciled: the first Producer to publish a named PUB message owns its payload schema; a different Producer may co-publish only an identical schema.
Request body
application/json
Schema: ProvideAsyncApiPayload.
Example request body (placeholder values derived from the schema)
{
"producername": "string",
"asyncapi_yaml": "string",
"stability": "snapshot",
"retired": false,
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string",
"dry_run": false,
"trunk": false,
"tag": "string"
}
Responses
| Status | Description | Body |
|---|---|---|
202 | Specification accepted and stored — or, for Header | one of ProvideResponse, RetiredProtocolResponse
Example 202 response (placeholder values derived from the schema) |
400 | Invalid request (e.g. malformed YAML, bad version). | ErrorBody |
403 |
| ErrorBody |
409 | Rejected by the version rules (see | ErrorBody |
413 | Request body exceeds the configured size limit ( | — |
/provide/grpc#
POST /provide/grpc#
Provide a .proto file under its declared version
Authentication: session or API token (full)
Same rules as /provide, for proto files. Proto has no standard version slot, so the file must carry exactly one // sanshain-version: MAJOR.MINOR.PATCH comment (conventionally in the header); a missing, malformed or conflicting marker rejects the Provide. The marker travels into the split files Consumers download.
Request body
application/json
Schema: ProvideProtoPayload.
Example request body (placeholder values derived from the schema)
{
"producername": "string",
"proto_content": "string",
"stability": "snapshot",
"retired": false,
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string",
"dry_run": false,
"trunk": false,
"tag": "string"
}
Responses
| Status | Description | Body |
|---|---|---|
202 | Specification accepted and stored — or, for Header | one of ProvideResponse, RetiredProtocolResponse
Example 202 response (placeholder values derived from the schema) |
400 | Invalid request (e.g. missing sanshain-version marker). | ErrorBody |
403 |
| ErrorBody |
409 | Rejected by the version rules (see | ErrorBody |
413 | Request body exceeds the configured size limit ( | — |
/require#
GET /require#
Retrieve an endpoint snippet at a pinned version
Authentication: session or API token (full)
Resolves one endpoint of the Producer's line at exactly the pinned version: GA preferred, else the same-numbered snapshot, else an immediate 404. There is no fallback to any other version and nothing waits — a missing pinned version is a configuration error.
A successful require records the dependency (this is what the graph shows) and counts as use for snapshot expiry. A failed require records nothing — resolution never creates graph entities.
Responses carry X-Sanshain-Resolution (served), X-Sanshain-Version (the Pin) and X-Sanshain-Stability (ga or snapshot — what actually answered), plus an ETag; If-None-Match yields 304 Not Modified when unchanged.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
consumername | query | yes | string | Name of the requiring Consumer. |
producername | query | yes | string | Name of the Producer to require from. |
version | query | yes | string | The exact pinned version ( |
path | query | yes | string | The OpenAPI path (lenient matching normalizes variable names). |
method | query | yes | string | The HTTP verb (case-insensitive). |
dry_run | query | — | boolean | Resolve without recording the dependency. Default |
trunk | query | — | boolean | Marks this build as the trunk stream's: the pin is additionally recorded in the append-only trunk store that feeds the main graph (last write per endpoint wins). Omit when not using trunk/graph features. Default |
tag | query | — | string | The pin updates the named sanshain-branch's graph instead of trunk (release-branch hotfix). Mutually exclusive with |
Responses
| Status | Description | Body |
|---|---|---|
200 | The endpoint snippet as YAML. Header | string (application/yaml)
Example 200 response (recorded from a live instance) |
304 | Not modified (ETag match). Header | — |
403 | The request carried an API token with scope | ErrorBody
Example 403 response (recorded from a live instance) |
404 | Unknown — the Producer, or the pinned version, does not exist in either stability. Fails in milliseconds; nothing waits for a version to appear. | ErrorBody |
410 | Absent — the pinned version exists and deliberately does not include this endpoint. Provided specs are complete, so absence is a definitive no, distinct from an unknown version. | ErrorBody |
/require/asyncapi#
GET /require/asyncapi#
Retrieve an AsyncAPI channel snippet at a pinned version
Authentication: session or API token (full)
Same resolution rules as /require. path is the channel name (AsyncAPI 2.x channel key / 3.x channel address); method is PUB or SUB from the provider's perspective.
With format=openapi the answer is not the AsyncAPI snippet but an OpenAPI 3.0.3 document holding the payload schemas of the channel's messages (see the format parameter). Its ETag never matches the AsyncAPI answer's.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
consumername | query | yes | string | |
producername | query | yes | string | |
version | query | yes | string | |
path | query | yes | string | |
method | query | yes | string | |
dry_run | query | — | boolean | Default |
trunk | query | — | boolean | See Default |
tag | query | — | string | See |
format | query | — | string |
One of |
Responses
| Status | Description | Body |
|---|---|---|
200 | The channel snippet as YAML, or with Header | string (application/yaml) |
304 | Not modified (ETag match). Header | — |
400 | An unknown | ErrorBody |
403 | The request carried an API token with scope | ErrorBody
Example 403 response (recorded from a live instance) |
404 | Unknown Producer or version. | ErrorBody |
410 | The pinned version deliberately lacks this channel. | ErrorBody |
/require/grpc#
GET /require/grpc#
Retrieve a gRPC method snippet at a pinned version
Authentication: session or API token (full)
Same resolution rules as /require. path is the fully qualified service name; method is the exact (case-sensitive) RPC name.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
consumername | query | yes | string | |
producername | query | yes | string | |
version | query | yes | string | |
path | query | yes | string | |
method | query | yes | string | |
dry_run | query | — | boolean | Default |
trunk | query | — | boolean | See Default |
tag | query | — | string | See |
Responses
| Status | Description | Body |
|---|---|---|
200 | The .proto snippet. Header | string (text/plain) |
304 | Not modified (ETag match). Header | — |
403 | The request carried an API token with scope | ErrorBody
Example 403 response (recorded from a live instance) |
404 | Unknown Producer or version. | ErrorBody |
410 | The pinned version deliberately lacks this method. | ErrorBody |
/require-bundle#
POST /require-bundle#
Retrieve multiple endpoint snippets as a single merged spec
Authentication: session or API token (full)
Resolves several endpoints of one Producer at one pinned version and returns a single merged document (one copy of each shared schema, so generated DTOs are shared across operations). Resolution rules match /require; if any requested endpoint is absent from the pinned version the whole bundle fails 410 naming the missing endpoints, and no dependency is recorded.
With format=openapi (query parameter, api_type: asyncapi only) the answer is one OpenAPI 3.0.3 document holding the payload schemas of all requested channels' messages, built as described on /require/asyncapi.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
format | query | — | string |
One of |
Request body
application/json
Schema: RequireBundlePayload.
Example request body (placeholder values derived from the schema)
{
"consumername": "string",
"producername": "string",
"version": "string",
"api_type": "openapi",
"endpoints": [
{
"path": "string",
"method": "string"
}
],
"dry_run": false,
"trunk": false,
"tag": "string"
}
Responses
| Status | Description | Body |
|---|---|---|
200 | The merged specification as YAML, or with Header | string (application/yaml) |
304 | Not modified (ETag match). Header | — |
400 |
| ErrorBody |
403 | The request carried an API token with scope | ErrorBody
Example 403 response (recorded from a live instance) |
404 | Unknown Producer or version. | ErrorBody |
410 | The pinned version deliberately lacks one or more requested endpoints. | ErrorBody |
/catalog/{systemname}#
PUT /catalog/{systemname}#
Record a system's periphery and context from its Backstage catalog-info.yaml
Authentication: session or API token (full)
Optional. A Producer's or Consumer's build pushes its Backstage catalog-info.yaml so the network graph can show the Peripheral Resources the system depends on — databases, buckets, queues and other things outside Sanshain — and so the graph's node panel and the system's page can say who owns it, what it is, where its code lives and where its wiki, dashboards and ticket board are.
Sanshain reads only the Component whose metadata.name equals systemname (case-insensitive), and of it only:
spec.dependsOn:resource:refs become Peripheral Resources;component:refs are recorded too and drawn as a missing component while no Producer or Consumer of that name exists (a ref with a Sanshain pendant is ignored: edges between systems are Sanshain's own,/require);consumesApisis ignored. A ref without a kind is read as aresource, a ref without a namespace takes the Component's namespace (defaultwhen it declares none), and refs are lower-cased.spec.owner, normalised like other refs (a bareteam-ordersisgroup:default/team-orders) and kept as kind and name.spec.system, the Backstage system the Component belongs to: its name fills the Producer's domain (the graph's cluster) when no domain is set; a domain an administrator set stays.metadata.description, trimmed, at most 1000 characters (a longer one is cut with an ellipsis).- the
backstage.io/source-locationannotation, without itsurl:prefix, when it is anhttporhttpsURL. metadata.links:url(must behttporhttps, otherwise the link is dropped),title(defaults to the URL's host),typeandicon; at most 20, further ones are ignored.metadata.tags, lower-cased and deduplicated, at most 20; shown as chips with the context, answered by the MCPdescribe_service, and a graph focus (tag:<name>).
Everything else in the file is ignored. Each push replaces the system's periphery and its context: a resource no longer listed is removed from the current graph (it stays in the timeline), an empty or missing dependsOn clears it, and a missing owner, description, source location or link list leaves that field empty. trunk: true updates the main graph and the dev graph; otherwise only the dev graph. The context is one present-day fact per system, whatever the stream. Sanshain-branches get the periphery copied when they are cut. A push whose content matches what is recorded changes nothing.
Any authenticated caller may push — the same bar as /require.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
systemname | path | yes | string | The system the file describes — the |
Request body
application/json
Schema: CatalogPushPayload.
Example request body (recorded from a live instance)
{
"catalog_info_yaml": "apiVersion: backstage.io/v1alpha1\nkind: Component\nmetadata:\n name: payments\n description: Takes payments for the shop and hands them to the bank.\n annotations:\n backstage.io/source-location: url:https://github.com/example/payments\nspec:\n type: service\n owner: group:default/checkout\n dependsOn:\n - resource:default/payments-db\n - resource:default/bank-gateway\n",
"trunk": true
}
Responses
| Status | Description | Body |
|---|---|---|
200 | The periphery now recorded for the system ( When the file is not valid YAML, a document in it is not a Backstage entity, it has no (or more than one) Header | CatalogPushResponse
Example 200 response (recorded from a live instance) |
403 | The request carried an API token with scope | ErrorBody
Example 403 response (recorded from a live instance) |
413 | Request body exceeds the configured size limit ( | — |
422 | Request body has the wrong shape (missing | — |
/producers/{producername}/versions#
GET /producers/{producername}/versions#
List a Producer's version lines
Authentication: session or API token (full or read-only)
Every version-line entry of one Producer — version, stability, content hash, timestamps, last provider, endpoint count, and (for snapshots, when cleanup is enabled) the use-based expiry. What "what can I upgrade to?" tooling reads.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
producername | path | yes | string | |
api_type | query | — | string | Narrow to one API type's line. One of |
Responses
| Status | Description | Body |
|---|---|---|
200 | The version-line entries, API types grouped, newest first. | ProducerVersionInfo[]
Example 200 response (recorded from a live instance) |
404 | Unknown Producer. | ErrorBody |
/report#
GET /report#
The dependency report of a graph scope
Authentication: session or API token (full or read-only)
Everything the graph view shows, as JSON: the dependency graph (Consumer pins at exact versions), provided endpoints nobody requires, required endpoints a pinned version does not provide, the trunk pins, harvested AsyncAPI subscriptions, Peripheral Resources and the catalog context of every system. scope selects the graph: dev (default) is accumulated activity, main the trunk, a sanshain-branch name its cut; main and a branch take @<instant> (RFC 3339) for a past state (ADR-0004, ADR-0005). Not audited.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
scope | query | — | string |
|
Responses
| Status | Description | Body |
|---|---|---|
200 | The report; | DependencyReport
Example 200 response (recorded from a live instance) |
400 |
| ErrorBody |
401 | Missing or invalid token. | — |
404 | No sanshain-branch of that name. | ErrorBody |
/report/markdown#
GET /report/markdown#
The dependency report as Markdown
Authentication: session or API token (full or read-only)
The same report as /report, rendered as a Markdown document for a wiki page or a release note. Audited as REPORT.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
scope | query | — | string |
|
Responses
| Status | Description | Body |
|---|---|---|
200 | The report. | string (text/markdown) |
400 | Unusable | — |
401 | Missing or invalid token. | — |
404 | No sanshain-branch of that name. | — |
/report/isolation#
GET /report/isolation#
The isolation report
Authentication: session or API token (full or read-only)
Which Consumers would be affected by which Producer, from the same report as /report, rendered as plain text. Audited as REPORT.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
scope | query | — | string |
|
Responses
| Status | Description | Body |
|---|---|---|
200 | The report. | string (text/plain) |
400 | Unusable | — |
401 | Missing or invalid token. | — |
404 | No sanshain-branch of that name. | — |
/endpoint-versions#
GET /endpoint-versions#
The history of one endpoint across a Producer's version line
Authentication: session or API token (full or read-only)
Walks the Producer's version line for one endpoint and reports, per version, what it stored for that endpoint and whether that version changed it: the blame trail behind "last changed in version X by Y".
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
service | query | yes | string | The Producer. |
api_type | query | — | string | One of Default |
path | query | yes | string | The endpoint's path (OpenAPI), channel (AsyncAPI) or |
method | query | yes | string | The HTTP method, |
Responses
| Status | Description | Body |
|---|---|---|
200 | Oldest version first. | EndpointHistoryEntry[]
Example 200 response (recorded from a live instance) |
401 | Missing or invalid token. | — |
404 | No such Producer. | ErrorBody |
/api/audit/timeline#
GET /api/audit/timeline#
Audit entries, newest first
Authentication: session token
The audit log the dashboard's timeline reads. Session only, with the view_audit permission (the admin and viewer roles); an API token is refused.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
limit | query | — | integer | Default |
from_date | query | — | string | RFC 3339 lower bound. |
to_date | query | — | string | RFC 3339 upper bound. |
action_type | query | — | string | Exact |
service | query | — | string | Service name, |
version | query | — | string | Version, |
stream | query | — | string | Exact stream, |
Responses
| Status | Description | Body |
|---|---|---|
200 | Newest first, at most | AuditLogEntry[]
Example 200 response (recorded from a live instance) |
401 | Not signed in, or an API token. | — |
403 | Signed in without | — |
/api/sse/updates#
GET /api/sse/updates#
Catalog change notifications as server-sent events
Authentication: session or API token (full or read-only)
A text/event-stream that sends the event updated whenever a provide, require, deletion or branch cut changed the catalog; the event carries no detail, the client re-reads what it shows. Keep-alive comments keep the connection open.
Responses
| Status | Description | Body |
|---|---|---|
200 | An open event stream; each event's data is | string (text/event-stream) |
401 | Missing or invalid token. | — |
/api/ws/updates#
GET /api/ws/updates#
Catalog change notifications over a WebSocket
Authentication: session or API token (full or read-only)
The WebSocket twin of /api/sse/updates: after the upgrade the server sends the text frame updated on every catalog change and closes when the client closes.
Responses
| Status | Description | Body |
|---|---|---|
101 | Switching protocols; text frames | — |
401 | Missing or invalid token. | — |
/validate#
POST /validate#
Validate a specification without publishing anything
Authentication: none
The free validator: runs exactly the provide-side pipeline — version extraction (MAJOR[.MINOR[.PATCH]], optional v prefix) and endpoint splitting — with no persistence and no producer context. Unauthenticated and stateless; bounded by the request body-size limit. Always answers 200 with a verdict, so callers parse one shape. "Valid here" means "a Provide of this document parses there" — the version-line rules (GA immutability, semver honesty) additionally apply on a real Provide.
Request body
application/json
| Field | Type | Required | Description |
|---|---|---|---|
api_type | string | yes | One of |
content | string | yes | The full specification document. |
Example request body (recorded from a live instance)
{
"api_type": "openapi",
"content": "openapi: 3.0.3\ninfo:\n title: Payments\n version: 1.0.0\npaths:\n /payments:\n post:\n summary: Create a payment\n responses:\n '201':\n description: Created\n"
}
Responses
| Status | Description | Body |
|---|---|---|
200 | The verdict. | ValidationReport
Example 200 response (recorded from a live instance) |
413 | Request body exceeds the configured size limit. | — |
/version#
GET /version#
Report the running Sanshain instance version
Authentication: none
The instance version — Sanshain's own build — never to be confused with spec versions.
Responses
| Status | Description | Body |
|---|---|---|
200 | Instance version and id. | object
Example 200 response (recorded from a live instance) |
/auth/tokens#
GET /auth/tokens#
List the caller's API tokens
Authentication: session token
Every token the signed-in user owns, newest first, with its scope and when it was last used. The raw token is never answered again after creation. Session-authenticated: this is the account page's call, so an API token cannot list or mint tokens.
Responses
| Status | Description | Body |
|---|---|---|
200 | The caller's tokens. | ApiToken[]
Example 200 response (placeholder values derived from the schema) |
401 | No valid session. | — |
POST /auth/tokens#
Create an API token
Authentication: session token
Mints a token for the signed-in user and answers it once. The scope is decided here and is immutable afterwards: full (the default) may do everything the user may; read_only is for AI / MCP clients and is refused on every route that changes the catalog (see the ReadOnlyToken response). Session-authenticated.
Request body
application/json
Schema: CreateApiTokenPayload.
Example request body (recorded from a live instance)
{
"name": "claude-code",
"expires_in_days": 90,
"scope": "read_only"
}
Responses
| Status | Description | Body |
|---|---|---|
200 | The new token. Copy | CreateApiTokenResponse
Example 200 response (recorded from a live instance) |
401 | No valid session. | — |
409 | The caller already has a token of that name. | ErrorBody |
/auth/tokens/{id}#
DELETE /auth/tokens/{id}#
Revoke an API token
Authentication: session token
Deletes one of the caller's tokens; it stops validating immediately. Session-authenticated.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | yes | string | The token id answered at creation and by the listing. |
Responses
| Status | Description | Body |
|---|---|---|
200 | Revoked. | — |
401 | No valid session. | — |
404 | No such token owned by the caller. | ErrorBody |
Schemas#
ErrorBody#
The body every error response carries. error holds the human-readable message; proposed_version appears only on 409s from the version rules.
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Human-readable description of what went wrong. |
proposed_version | string | — | The next free version the Producer should publish as instead. Present only on a 409 refusing a Provide by the version rules. |
Example (placeholder values derived from the schema)
{
"error": "string",
"proposed_version": "string"
}
ApiTokenScope#
What a token may do. Decided at creation, immutable afterwards. full is everything its owner may do; read_only is for AI / MCP clients and is refused on every catalog-changing route.
Type: string. One of full, read_only.
ApiToken#
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | |
user_id | integer (int64) | — | |
name | string | yes | |
scope | ApiTokenScope | yes | What a token may do. Decided at creation, immutable afterwards. One of Default |
created_at | string | yes | RFC 3339 instant. |
expires_at | string | yes | RFC 3339 instant; the token stops validating after it. |
last_used_at | string | null | — | When the token last authenticated a request (API or MCP), to the minute — the stamp is refreshed at most once a minute. |
Example (placeholder values derived from the schema)
{
"id": "string",
"user_id": 1,
"name": "string",
"scope": "full",
"created_at": "string",
"expires_at": "string",
"last_used_at": "string"
}
CreateApiTokenPayload#
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Unique per user. |
expires_in_days | integer | yes | |
scope | ApiTokenScope | — | Omitted means One of Default |
Example (placeholder values derived from the schema)
{
"name": "string",
"expires_in_days": 0,
"scope": "full"
}
CreateApiTokenResponse#
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | |
name | string | yes | |
scope | ApiTokenScope | yes | What a token may do. Decided at creation, immutable afterwards. One of Default |
token | string | yes | The raw token ( |
Example (placeholder values derived from the schema)
{
"id": "string",
"name": "string",
"scope": "full",
"token": "string"
}
Stability#
Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number.
Type: string. One of snapshot, ga.
ProvidePayload#
| Field | Type | Required | Description |
|---|---|---|---|
producername | string | yes | Name of the Producer providing the specification. |
openapi_yaml | string | — | The full OpenAPI specification as a YAML string. Its |
stability | Stability | — | Required, except on a retire, which publishes nothing and so has no stability to declare. One of |
retired | boolean | — | The Producer no longer provides this API family — retire the capability instead of publishing a version. The endpoint already names the family, so nothing else identifies it. Retiring clears the Retiring is a role, like releasing: the caller needs the Combining it with anything a publish needs answers With Default |
build_url | string | null | — | Where this build ran (an absolute |
change_url | string | null | — | The pull or merge request behind this build (an absolute |
tickets | string[] | — | Ticket ids this build closes (letters, digits, |
note | string | null | — | One line of intent, at most 200 characters. |
dry_run | boolean | — | Validate and classify without storing anything. Default |
trunk | boolean | — | Marks this build as the trunk stream's: the stored version entry becomes "trunk's current version" (shown in the main graph), as opposed to e.g. a release-branch GA backport. Orthogonal to Default |
tag | string | — | Marks this build as a sanshain-branch's (release-branch hotfix): the producer's member version within that branch is updated. Mutually exclusive with |
Example (placeholder values derived from the schema)
{
"producername": "string",
"openapi_yaml": "string",
"stability": "snapshot",
"retired": false,
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string",
"dry_run": false,
"trunk": false,
"tag": "string"
}
ProvideAsyncApiPayload#
| Field | Type | Required | Description |
|---|---|---|---|
producername | string | yes | Name of the Producer. |
asyncapi_yaml | string | — | The full AsyncAPI document as a YAML string. Its |
stability | Stability | — | See One of |
retired | boolean | — | See Default |
build_url | string | null | — | See |
change_url | string | null | — | See |
tickets | string[] | — | See |
note | string | null | — | See |
dry_run | boolean | — | Default |
trunk | boolean | — | See Default |
tag | string | — | See |
Example (placeholder values derived from the schema)
{
"producername": "string",
"asyncapi_yaml": "string",
"stability": "snapshot",
"retired": false,
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string",
"dry_run": false,
"trunk": false,
"tag": "string"
}
ProvideProtoPayload#
| Field | Type | Required | Description |
|---|---|---|---|
producername | string | yes | Name of the Producer. |
proto_content | string | — | The full .proto file. Must carry exactly one |
stability | Stability | — | See One of |
retired | boolean | — | See Default |
build_url | string | null | — | See |
change_url | string | null | — | See |
tickets | string[] | — | See |
note | string | null | — | See |
dry_run | boolean | — | Default |
trunk | boolean | — | See Default |
tag | string | — | See |
Example (placeholder values derived from the schema)
{
"producername": "string",
"proto_content": "string",
"stability": "snapshot",
"retired": false,
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string",
"dry_run": false,
"trunk": false,
"tag": "string"
}
RetiredProtocolResponse#
What retiring an API family actually shed. Answered by a provide call carrying retired: true, in place of ProvideResponse — a retire publishes no version, so it reports no version.
All three counts are zero under dry_run, which checks permission and stops without retiring anything.
| Field | Type | Required | Description |
|---|---|---|---|
tag_cleared | string | null | — | The capability auto-tag removed — |
trunk_pins_closed | integer | yes | Open trunk pins closed. They leave the current main graph; their closed rows stay as timeline history, so the era the Producer did provide this family remains visible. |
contracts_released | integer | yes | AsyncAPI channel-message contracts released for another Producer to claim. Always zero for OpenAPI and proto, which own no contracts. |
Example (placeholder values derived from the schema)
{
"tag_cleared": "string",
"trunk_pins_closed": 0,
"contracts_released": 0
}
ProvideResponse#
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | The version read from the provided document. |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
content_hash | string | yes | SHA-256 ( |
changes | object | yes | Endpoint diff relative to what this exact version stored before (snapshot overwrite / promotion); a brand-new version reports its whole endpoint set as inserts. All zero = idempotent no-op. |
changes.inserts | integer | — | |
changes.updates | integer | — | |
changes.deletes | integer | — | |
promoted | boolean | — | True when this Provide released an existing snapshot in place (snapshot → GA under the same number), including the byte-identical case where |
harvested_subscriptions | object[] | — | AsyncAPI Perspective convention: Sanshain reads AsyncAPI 2.x |
harvested_subscriptions[].channel | string | yes | |
harvested_subscriptions[].message_name | string | yes | |
harvested_subscriptions[].owner | string | null | — | The Producer that owns the channel's PUB contract, or absent when no GA provider owns it yet (an unfulfilled subscription). |
harvested_subscriptions[].drift | string | null | — | Advisory note when the subscription's expectation is not satisfiable by the current contract (snapshot provides only). |
Example (placeholder values derived from the schema)
{
"version": "string",
"stability": "snapshot",
"content_hash": "string",
"changes": {
"inserts": 0,
"updates": 0,
"deletes": 0
},
"promoted": false,
"harvested_subscriptions": [
{
"channel": "string",
"message_name": "string",
"owner": "string",
"drift": "string"
}
]
}
RequireBundlePayload#
| Field | Type | Required | Description |
|---|---|---|---|
consumername | string | yes | Name of the requiring Consumer. |
producername | string | yes | Name of the Producer. |
version | string | yes | The exact pinned version ( |
api_type | string | — | One of Default |
endpoints | object[] | yes | |
endpoints[].path | string | yes | |
endpoints[].method | string | yes | |
dry_run | boolean | — | Default |
trunk | boolean | — | See Default |
tag | string | — | See |
Example (placeholder values derived from the schema)
{
"consumername": "string",
"producername": "string",
"version": "string",
"api_type": "openapi",
"endpoints": [
{
"path": "string",
"method": "string"
}
],
"dry_run": false,
"trunk": false,
"tag": "string"
}
CatalogPushPayload#
| Field | Type | Required | Description |
|---|---|---|---|
catalog_info_yaml | string | yes | The system's Backstage |
trunk | boolean | — | A trunk build (ADR-0004): the periphery updates the main graph as well as the dev graph. Default |
Example (placeholder values derived from the schema)
{
"catalog_info_yaml": "string",
"trunk": false
}
CatalogPushResponse#
| Field | Type | Required | Description |
|---|---|---|---|
system | string | yes | |
trunk | boolean | yes | |
resources | string[] | yes | The recorded refs, |
components | string[] | — | The recorded |
recorded | boolean | yes | Whether the push was recorded. |
Example (placeholder values derived from the schema)
{
"system": "string",
"trunk": false,
"resources": [
"string"
],
"components": [
"string"
],
"recorded": false
}
ValidationReport#
| Field | Type | Required | Description |
|---|---|---|---|
valid | boolean | yes | |
api_type | string | yes | One of |
version | string | — | The version the document declares, when parseable. |
endpoint_count | integer | — | How many endpoints/channels/RPCs the split found. |
endpoints | string[] | — | Preview of the first entries the split would store, as "METHOD path" strings (at most 20). |
error | string | — | Why the document is invalid; absent when valid. |
Example (placeholder values derived from the schema)
{
"valid": false,
"api_type": "openapi",
"version": "string",
"endpoint_count": 0,
"endpoints": [
"string"
],
"error": "string"
}
VersionOrigin#
What the build that provided a version said about why: references, not content. The latest Provide that carries any of them wins; a no-op re-provide of identical bytes only fills an empty one. All fields absent when no build said anything.
| Field | Type | Required | Description |
|---|---|---|---|
build_url | string | null | yes | |
change_url | string | null | yes | |
tickets | string[] | yes | |
note | string | null | yes |
Example (placeholder values derived from the schema)
{
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string"
}
ProducerVersionInfo#
| Field | Type | Required | Description |
|---|---|---|---|
origin | VersionOrigin | yes | What the build that provided a version said about why: references, not content. The latest Provide that carries any of them wins; a no-op re-provide of identical bytes only fills an empty one. All fields absent when no build said anything. |
api_type | string | yes | One of |
version | string | yes | |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
content_hash | string | yes | |
provided_by | string | yes | The authenticated Actor credited with this version's content — the last provider. A same-content promotion preserves the snapshot's provider, so the human who built it stays on the released version. |
created_at | string | yes | When this version first appeared on the line. |
updated_at | string | yes | Last successful Provide (overwrite or promotion). |
last_required_at | string | null | — | Last successful require against this version. |
endpoint_count | integer (int64) | yes | |
expires_at | string | null | — | Use-based expiry for snapshots when cleanup is enabled; absent for GA (never age-culled) or when cleanup is off. |
Example (placeholder values derived from the schema)
{
"origin": {
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string"
},
"api_type": "openapi",
"version": "string",
"stability": "snapshot",
"content_hash": "string",
"provided_by": "string",
"created_at": "string",
"updated_at": "string",
"last_required_at": "string",
"endpoint_count": 1,
"expires_at": "string"
}
WebhookEnvelope#
The JSON a webhook receiver gets (#47) — the body of the json format, and the facts behind the slack and teams bodies. No path serves it: Sanshain POSTs it to the URL a hook was registered with, signed with Sanshain-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>"> under the hook's secret, with Sanshain-Event (the type) and X-Sanshain-Delivery (the delivery id) alongside. See the CI integration guide for the receiver's side.
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | yes | Unique per event; a redelivery carries the same id. |
type | WebhookEventType | yes | One of |
occurred_at | string (date-time) | yes | RFC 3339, UTC, second precision. |
sanshain_version | string | yes | The instance version that emitted the event. |
data | one of WebhookVersionPublishedData, WebhookProvideRejectedData, WebhookVersionDeprecatedData, WebhookDependencyRecordedData, WebhookBranchCutData, WebhookCatalogRecordedData, WebhookTestData | yes | Per type — see the |
links | map of string (uri) | — | Deep links into the UI, keyed |
Example (placeholder values derived from the schema)
{
"id": "string",
"type": "version.published",
"occurred_at": "2026-01-31T12:00:00Z",
"sanshain_version": "string",
"data": {
"producer": "string",
"api_type": "openapi",
"version": "string",
"stability": "snapshot",
"promoted": false,
"stream": "string",
"actor": "string",
"compatibility": {
"against": "string",
"impact": "none"
},
"origin": {
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string"
}
},
"links": {
"key": "https://example.org/"
}
}
WebhookEventType#
Type: string. One of version.published, provide.rejected, version.deprecated, dependency.recorded, branch.cut, catalog.recorded, webhook.test.
WebhookStream#
dev, trunk, or a Sanshain-branch's name.
Type: string.
WebhookVersionPublishedData#
A Provide succeeded. The idempotent no-op emits nothing.
| Field | Type | Required | Description |
|---|---|---|---|
producer | string | yes | |
api_type | string | yes | One of |
version | string | yes | |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
promoted | boolean | yes | A Snapshot was promoted to GA in place. |
stream | WebhookStream | yes |
|
actor | string | null | yes | |
compatibility | object | null | yes | On a GA Provide with a previous GA on the line — the classification against it. |
compatibility.against | string | yes | |
compatibility.impact | string | yes | One of |
origin | VersionOrigin | yes | What the build that provided a version said about why: references, not content. The latest Provide that carries any of them wins; a no-op re-provide of identical bytes only fills an empty one. All fields absent when no build said anything. |
Example (placeholder values derived from the schema)
{
"producer": "string",
"api_type": "openapi",
"version": "string",
"stability": "snapshot",
"promoted": false,
"stream": "string",
"actor": "string",
"compatibility": {
"against": "string",
"impact": "none"
},
"origin": {
"build_url": "string",
"change_url": "string",
"tickets": [
"string"
],
"note": "string"
}
}
WebhookProvideRejectedData#
A Provide was refused by a version rule. Dry-runs emit nothing.
| Field | Type | Required | Description |
|---|---|---|---|
producer | string | yes | |
api_type | string | yes | One of |
version | string | yes | The attempted Version. |
rule | string | yes | One of |
message | string | yes | |
proposed_version | string | null | yes | The next free Version to publish as instead, where one exists. |
actor | string | null | yes |
Example (placeholder values derived from the schema)
{
"producer": "string",
"api_type": "openapi",
"version": "string",
"rule": "immutable_ga",
"message": "string",
"proposed_version": "string",
"actor": "string"
}
WebhookVersionDeprecatedData#
A Provide newly marked elements deprecated (not deprecated in the previous GA).
| Field | Type | Required | Description |
|---|---|---|---|
producer | string | yes | |
api_type | string | yes | One of |
version | string | yes | |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
elements | object[] | yes | |
elements[].path | string | yes | |
elements[].method | string | yes | |
document_deprecated | boolean | yes | Every element of the document is deprecated. |
Example (placeholder values derived from the schema)
{
"producer": "string",
"api_type": "openapi",
"version": "string",
"stability": "snapshot",
"elements": [
{
"path": "string",
"method": "string"
}
],
"document_deprecated": false
}
WebhookDependencyRecordedData#
A require recorded an edge the Consumer did not hold yet in the declared stream.
| Field | Type | Required | Description |
|---|---|---|---|
consumer | string | yes | |
producer | string | yes | |
api_type | string | yes | One of |
version | string | yes | The Pin. |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
snapshot_pinned | boolean | yes | |
stream | WebhookStream | yes |
|
endpoints | object[] | yes | The endpoints new to the edge. |
endpoints[].path | string | yes | |
endpoints[].method | string | yes |
Example (placeholder values derived from the schema)
{
"consumer": "string",
"producer": "string",
"api_type": "openapi",
"version": "string",
"stability": "snapshot",
"snapshot_pinned": false,
"stream": "string",
"endpoints": [
{
"path": "string",
"method": "string"
}
]
}
WebhookBranchCutData#
A Sanshain-branch was created. Global hooks only.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | |
source | string | yes |
|
as_of | string (date-time) | yes | |
cut_at | string (date-time) | yes | |
actor | string | yes |
Example (placeholder values derived from the schema)
{
"name": "string",
"source": "string",
"as_of": "2026-01-31T12:00:00Z",
"cut_at": "2026-01-31T12:00:00Z",
"actor": "string"
}
WebhookCatalogRecordedData#
A catalog push ran, readable or not.
| Field | Type | Required | Description |
|---|---|---|---|
system | string | yes | |
trunk | boolean | yes | |
recorded | boolean | yes | |
warning | string | null | yes | Why nothing was recorded, when |
resources | integer | yes | The number of Peripheral Resources recorded. |
Example (placeholder values derived from the schema)
{
"system": "string",
"trunk": false,
"recorded": false,
"warning": "string",
"resources": 0
}
WebhookTestData#
The "send test event" button, to one hook alone.
| Field | Type | Required | Description |
|---|---|---|---|
hook_id | integer (int64) | yes | |
message | string | yes |
Example (placeholder values derived from the schema)
{
"hook_id": 1,
"message": "string"
}
ReportEndpoint#
A provided endpoint that no Consumer requires.
| Field | Type | Required | Description |
|---|---|---|---|
api_type | string | yes | One of |
service | string | yes | |
version | string | yes | Semantic version, e.g. |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
path | string | yes | |
method | string | yes | |
deprecated | boolean | yes |
Example (placeholder values derived from the schema)
{
"api_type": "openapi",
"service": "string",
"version": "string",
"stability": "snapshot",
"path": "string",
"method": "string",
"deprecated": false
}
ReportMissingEndpoint#
A required endpoint the pinned version does not provide.
| Field | Type | Required | Description |
|---|---|---|---|
api_type | string | yes | One of |
client | string | yes | |
service | string | yes | |
version | string | yes | Semantic version, e.g. |
path | string | yes | |
method | string | yes |
Example (placeholder values derived from the schema)
{
"api_type": "openapi",
"client": "string",
"service": "string",
"version": "string",
"path": "string",
"method": "string"
}
ReportDependency#
One Consumer-to-Producer edge of the dependency graph, one endpoint at a pinned version.
| Field | Type | Required | Description |
|---|---|---|---|
api_type | string | yes | One of |
client | string | yes | |
service | string | yes | |
version | string | yes | Semantic version, e.g. |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
path | string | yes | |
method | string | yes | |
deprecated | boolean | yes |
Example (placeholder values derived from the schema)
{
"api_type": "openapi",
"client": "string",
"service": "string",
"version": "string",
"stability": "snapshot",
"path": "string",
"method": "string",
"deprecated": false
}
ReportTrunkPin#
One trunk pin (ADR-0004): what a Consumer declared as its production dependency.
| Field | Type | Required | Description |
|---|---|---|---|
client | string | yes | |
service | string | yes | |
api_type | string | yes | One of |
version | string | yes | Semantic version, e.g. |
path | string | yes | |
method | string | yes | |
valid_from | string | yes | |
last_required_at | string | yes | |
dangling | boolean | yes |
Example (placeholder values derived from the schema)
{
"client": "string",
"service": "string",
"api_type": "openapi",
"version": "string",
"path": "string",
"method": "string",
"valid_from": "string",
"last_required_at": "string",
"dangling": false
}
ReportHarvestedSubscription#
An AsyncAPI subscription harvested from a provided spec (ADR-0006).
| Field | Type | Required | Description |
|---|---|---|---|
client | string | yes | |
channel | string | yes | |
message_name | string | yes | |
owner | string | null | yes | |
trunk | boolean | yes |
Example (placeholder values derived from the schema)
{
"client": "string",
"channel": "string",
"message_name": "string",
"owner": "string",
"trunk": false
}
ReportPeripheralDependency#
An edge from a system to a Peripheral Resource (resource: ref) or a missing component (component: ref with no Sanshain pendant) from its Backstage catalog file.
| Field | Type | Required | Description |
|---|---|---|---|
system | string | yes | |
resource | string | yes |
Example (placeholder values derived from the schema)
{
"system": "string",
"resource": "string"
}
EndpointHistoryEntry#
One version of a Producer's line and what it stored for the endpoint; changed marks the versions that altered it (the blame trail).
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | Semantic version, e.g. |
stability | Stability | yes | Which of exactly two states a stored version is in. Declared by the caller on every Provide; a state of the stored version, never part of the version string. Snapshots are overwritable and expire when unused; GA is immutable and permanently claims its number. One of |
provided_by | string | yes | |
updated_at | string | yes | |
yaml_content | string | null | yes | |
changed | boolean | yes |
Example (placeholder values derived from the schema)
{
"version": "string",
"stability": "snapshot",
"provided_by": "string",
"updated_at": "string",
"yaml_content": "string",
"changed": false
}
AuditLogEntry#
One audit entry.
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | |
timestamp | string | yes | |
username | string | yes | |
action | string | yes | |
details | string | yes | |
service | string | null | yes | |
version | string | null | yes | |
action_type | string | null | yes | |
diff | string | null | yes | |
stream | string | null | yes |
Example (placeholder values derived from the schema)
{
"id": 0,
"timestamp": "string",
"username": "string",
"action": "string",
"details": "string",
"service": "string",
"version": "string",
"action_type": "string",
"diff": "string",
"stream": "string"
}
SystemContext#
What a system's Backstage catalog-info.yaml says about it: owner, description, source location, links and tags, as pushed to /catalog/{systemname}.
| Field | Type | Required | Description |
|---|---|---|---|
owner | object | null | yes | |
owner.kind | string | yes | |
owner.name | string | yes | |
description | string | null | yes | |
source_location | string | null | yes | |
links | object[] | yes | |
links[].url | string | yes | |
links[].title | string | yes | |
links[].type | string | null | yes | |
links[].icon | string | null | yes | |
tags | string[] | yes |
Example (placeholder values derived from the schema)
{
"owner": {
"kind": "string",
"name": "string"
},
"description": "string",
"source_location": "string",
"links": [
{
"url": "string",
"title": "string",
"type": "string",
"icon": "string"
}
],
"tags": [
"string"
]
}
DependencyReport#
The dependency report of one graph scope.
| Field | Type | Required | Description |
|---|---|---|---|
unused_endpoints | ReportEndpoint[] | yes | |
missing_endpoints | ReportMissingEndpoint[] | yes | |
dependency_graph | ReportDependency[] | yes | |
service_tags | map of string[] | yes | Tags per service name. |
trunk_graph | ReportTrunkPin[] | yes | |
harvested_subscriptions | ReportHarvestedSubscription[] | yes | |
peripheral_dependencies | ReportPeripheralDependency[] | yes | |
systems | map of SystemContext | yes | Catalog context per system name. |
trunk_stale_before | string | — | Only in the dev scope; trunk pins last required before this instant are stale. |
scope_label | string | — | The selected scope ( |
Example (placeholder values derived from the schema)
{
"unused_endpoints": [
{
"api_type": "openapi",
"service": "string",
"version": "string",
"stability": "snapshot",
"path": "string",
"method": "string",
"deprecated": false
}
],
"missing_endpoints": [
{
"api_type": "openapi",
"client": "string",
"service": "string",
"version": "string",
"path": "string",
"method": "string"
}
],
"dependency_graph": [
{
"api_type": "openapi",
"client": "string",
"service": "string",
"version": "string",
"stability": "snapshot",
"path": "string",
"method": "string",
"deprecated": false
}
],
"service_tags": {
"key": [
"string"
]
},
"trunk_graph": [
{
"client": "string",
"service": "string",
"api_type": "openapi",
"version": "string",
"path": "string",
"method": "string",
"valid_from": "string",
"last_required_at": "string",
"dangling": false
}
],
"harvested_subscriptions": [
{
"client": "string",
"channel": "string",
"message_name": "string",
"owner": "string",
"trunk": false
}
],
"peripheral_dependencies": [
{
"system": "string",
"resource": "string"
}
],
"systems": {
"key": {
"owner": {
"kind": "string",
"name": "string"
},
"description": "string",
"source_location": "string",
"links": [
{
"url": "string",
"title": "string",
"type": "string",
"icon": "string"
}
],
"tags": [
"string"
]
}
},
"trunk_stale_before": "string",
"scope_label": "string"
}