Model API¶
The Model API answers questions about your architecture as JSON. Scripts, CI (continuous integration) jobs, dashboards, AI assistants, or a person with curl, can ask “what talks to the database?” without opening the viewer and without reading the Python code of the model.
buildzr serve provides a Model API for every workspace it serves, at api/v1/ below the address of the workspace: the workspace microservices-example has its viewer at /microservices-example/ and its Model API at /microservices-example/api/v1/. The API is read-only: it answers only GET and HEAD requests.
This page is a reference of the API: the orientation document, every endpoint with an example, the limits, the errors, caching, browser access and the links to the viewer.
Start buildzr serve with the Model API¶
Serve a workspace; buildzr serve builds it first. Here, the model file is examples/buildzr_models_example.py from the buildzr repository:
$ buildzr serve examples/buildzr_models_example.py
built .buildzr/build/microservices-example (25 elements, 8 relationships, 2 views; modelHash sha256:1e2b294431af…)
Serving .buildzr/build at http://127.0.0.1:8000/ (Ctrl+C to stop)
http://127.0.0.1:8000/microservices-example/
Workspace list at http://127.0.0.1:8000/_buildzr/v1/workspaces
Then send a request with any HTTP client, for example curl -s http://127.0.0.1:8000/microservices-example/api/v1/. The answer is the orientation document.
The examples on this page are real answers from that workspace. They show the default output of buildzr serve, without --public-url. In the text, paths such as api/v1/tree are relative to the address of the workspace.
Words used on this page¶
| Word | Meaning |
|---|---|
| element | A person, a software system, a container (an application or data store inside a system) or a component (a part inside a container). Custom elements (type Element, such as a device) and deployment elements are elements too (see the next rows). |
| custom element | An element outside the C4 hierarchy, such as a piece of hardware. Its type is Element; it has no parent and no children. When the model gives it metadata (the word in brackets under its name in a diagram), the element answer has a metadata field; without metadata there is no such field. |
| deployment element | A deployment node (a server, cluster, region…), an infrastructure node (a load balancer, DNS…), or a container or software system instance (that container or system running on a node). Its id starts with deploy/<environment>/, for example deploy/production/aws/ecs-cluster/web-app. |
| id | The element’s path of lower-case names, joined by /: e-commerce-system/web-app is the Web App inside the E-Commerce System. Two siblings with the same name get -2, -3. Ids are given in query parameters: ?id=e-commerce-system/web-app. |
| relationship | An explicit “A uses B” statement written in the model, with a description and sometimes a technology. Its id is <source id>-><destination id>, plus :<description> when two relationships connect the same pair. |
| rollup | An arrow between two big boxes stands for every relationship between what is inside them. This API follows the same rule as the viewer: a relationship of a descendant counts for its ancestors. |
| view | A diagram the authors chose to draw (for example a system context view), as opposed to the free browsing the viewer offers. |
| modelHash | A fingerprint of the model, shown as sha256: and a hash. The same model always has the same hash, and every answer carries it. |
| workspace | A Workspace(...) written in Python. buildzr serve serves each one at /<name>/, with its own Model API. |
| viewer | The web page of a workspace that draws the diagrams. A data-only workspace has no viewer. |
The orientation document¶
GET api/v1/ returns the orientation document. It tells a reader what the model is, how big it is, and which requests it can send. Start here.
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"name": "microservices-example",
"description": null,
"status": "ok",
"builtWith": {
"buildzr": "1.0.0",
"k": 4
},
"counts": {
"elements": 25,
"relationships": 8,
"views": 2,
"byType": {
"Component": 4,
"Container": 5,
"ContainerInstance": 5,
"DeploymentNode": 6,
"InfrastructureNode": 1,
"Person": 2,
"SoftwareSystem": 2
}
},
"tags": [
"Component",
"Container",
"Container Instance",
"Deployment Node",
"Element",
"Infrastructure Node",
"Person",
"Software System"
],
"topLevel": [
"customer",
"administrator",
"e-commerce-system",
"payment-provider",
"deploy/production/aws"
],
"environments": [
{
"key": "production",
"name": "Production",
"nodes": [
"deploy/production/aws"
],
"views": [
"deployment-view-production-ecommerce"
]
}
],
"viewerUrl": "/microservices-example/",
"publicUrlConfigured": false,
"howToAsk": "Element ids are slug paths such as 'e-commerce-system/web-app'; give them in query parameters. Relationships are the explicit ones in the model. A relationship of a descendant counts for its ancestors, so an arrow between two systems is the union of the relationships between their contents. Deployment nodes, infrastructure nodes and instances are elements too, with ids under deploy/<environment>/ (see environments); an instance names its container or system in instanceOf. Lists are paged: follow 'next' while 'truncated' is true.",
"endpoints": {
"element": "/microservices-example/api/v1/element?id={id}",
"search": "/microservices-example/api/v1/elements{?q,type,tag,within,limit,cursor}",
"tree": "/microservices-example/api/v1/tree{?id,depth,limit}",
"neighbours": "/microservices-example/api/v1/neighbours?id={id}{&direction,depth,type,limit}",
"path": "/microservices-example/api/v1/path?from={from}&to={to}{&maxHops,limit}",
"relationships": "/microservices-example/api/v1/relationships{?from,to,edge,tag,limit,cursor}",
"views": "/microservices-example/api/v1/views{?scope,environment}",
"view": "/microservices-example/api/v1/view?key={key}"
},
"limits": {
"elements": {
"default": 50,
"max": 200
},
"relationships": {
"default": 50,
"max": 200
},
"neighbours": {
"default": 200,
"max": 1000
},
"tree": {
"default": 500,
"max": 1000
},
"pathChains": {
"default": 3,
"max": 5
},
"maxDepth": 4,
"maxHops": 8,
"maxQueryLength": 200
},
"notes": []
}
counts,tagsandtopLeveldescribe the model;topLevellists the ids to start from, the top-level deployment nodes included.environmentslists the deployment environments:key(used in deployment element ids and inviews?environment=),name, the top-level deploymentnodesand the deploymentviews.endpointsare URL templates: replace the{...}parts. Parameters after?or&inside braces are optional.limitsare the largest values each parameter accepts (see Limits).viewerUrlis the address of the viewer. It isnullwhen the workspace has no viewer (noindex.html), andnotessays so.builtWithisnullwhen the workspace has nobuild.json.statusisok, orbrokenwhen building the workspace failed (see When a workspace is broken). A broken workspace that still has its data, because only its checks failed, answers every request as usual, but its orientation document also hasstage(check),errors(the first 10) and a sentence innotes, and its answers may be wrong. A broken workspace without data answers 503, as below.
Endpoints¶
All of them are GET requests below /<name>/api/v1/, where <name> is the name of the workspace. Element ids go in query parameters.
| Path | Parameters | Answer |
|---|---|---|
| `` (empty) | none | The orientation document. |
element |
id (required) |
One element with all its fields, its childCount, the number of relationships in and out (its own, and with its descendants), and its viewerUrl. |
elements |
q (text, at most 200 characters), type, tag (repeatable: all must match), within (an id: only its descendants), limit (1 to 200, default 50), cursor |
Matching elements, best match first: exact name, name starts with, name contains, then description, technology, tags, properties. Not case sensitive. Without q the model order is kept. |
tree |
id (default: the top level), depth (1 to 4, default 1), limit (1 to 1000, default 500) |
The element and its descendants as a nested list. With id, depth=1 shows its children; without it, depth=1 shows the top level. children is null where the walk stopped and [] for a leaf. |
neighbours |
id (required), direction (out, in or both, default both), depth (1 to 4, default 1), type, limit (1 to 1000, default 200) |
The relationships that leave (or enter) the element or any of its descendants, each with the element at the other end and the hop number. Depth 2 and more continue from those elements the same way. type filters the elements at the other end. |
path |
from, to (required), maxHops (1 to 8, default 6), limit (1 to 5 chains, default 3) |
The shortest chains of explicit relationships from from (or something inside it) to to (or something inside it). All chains have the same, shortest length. hops is null when there is none within maxHops. |
relationships |
from and/or to (ids; each includes what is inside), or edge (an arrow id from the viewer, such as a->b), tag (repeatable), limit (1 to 200, default 50), cursor |
Explicit relationships. With both from and to this is exactly the rollup the viewer draws for the arrow between the two. |
views |
scope (an id), environment (an environment key) |
The authored views: key, kind, title, description, scope, environment (deployment views; null for the others), viewerUrl. |
view |
key (required) |
One view with the elements it shows and the ids of the relationships drawn in it. |
Unknown parameters are errors, so a typo does not silently give the wrong answer. A parameter that is not repeatable cannot be given twice.
Endpoint index¶
The orientation document lists the same endpoints as URL templates. This table is written from that document by scripts/gen_api_reference.py, so it follows the code. Do not edit the lines between the two markers; run the script instead. Names in the first column are the keys of endpoints in the orientation document (search is the path elements).
| Name | Template | Required | Optional | limit (default / largest) |
|---|---|---|---|---|
element |
/<name>/api/v1/element?id={id} |
id |
none | none |
search |
/<name>/api/v1/elements{?q,type,tag,within,limit,cursor} |
none | q, type, tag, within, limit, cursor |
50 / 200 |
tree |
/<name>/api/v1/tree{?id,depth,limit} |
none | id, depth, limit |
500 / 1000 |
neighbours |
/<name>/api/v1/neighbours?id={id}{&direction,depth,type,limit} |
id |
direction, depth, type, limit |
200 / 1000 |
path |
/<name>/api/v1/path?from={from}&to={to}{&maxHops,limit} |
from, to |
maxHops, limit |
3 / 5 |
relationships |
/<name>/api/v1/relationships{?from,to,edge,tag,limit,cursor} |
none | from, to, edge, tag, limit, cursor |
50 / 200 |
views |
/<name>/api/v1/views{?scope,environment} |
none | scope, environment |
none |
view |
/<name>/api/v1/view?key={key} |
key |
none | none |
element¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"element": {
"id": "e-commerce-system/web-app",
"modelId": "4",
"type": "Container",
"parent": "e-commerce-system",
"children": [],
"name": "Web App",
"description": null,
"technology": "React",
"tags": [
"Element",
"Container"
],
"properties": {},
"url": null,
"group": "E-Commerce Platform",
"views": [],
"childCount": 0,
"relationships": {
"own": {
"in": 2,
"out": 1
},
"withDescendants": {
"in": 2,
"out": 1
}
},
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/web-app"
}
}
relationships.own counts the relationships written on the element itself. withDescendants counts those that cross the element’s border: one end inside it, the other outside.
elements¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"total": 3,
"items": [
{
"id": "e-commerce-system/database",
"name": "Database",
"type": "Container",
"parent": "e-commerce-system",
"description": null,
"technology": "MongoDB",
"tags": [
"Element",
"Container"
],
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/database",
"matched": "technology"
},
{
"id": "deploy/production/aws/documentdb",
"name": "DocumentDB",
"type": "DeploymentNode",
"parent": "deploy/production/aws",
"description": null,
"technology": "MongoDB-compatible",
"tags": [
"Element",
"Deployment Node"
],
"childCount": 1,
"viewerUrl": "/microservices-example/#/v/deployment-view-production-ecommerce?sel=deploy/production/aws/documentdb",
"matched": "technology"
},
{
"id": "deploy/production/aws/documentdb/database",
"name": "Database",
"type": "ContainerInstance",
"parent": "deploy/production/aws/documentdb",
"description": null,
"technology": "MongoDB",
"tags": [
"Container Instance"
],
"childCount": 0,
"viewerUrl": "/microservices-example/#/v/deployment-view-production-ecommerce?sel=deploy/production/aws/documentdb/database",
"matched": "technology"
}
],
"next": null,
"truncated": false
}
matched says which field matched. total is the number of matches before paging.
tree¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"id": "e-commerce-system",
"depth": 2,
"items": [
{
"id": "e-commerce-system",
"name": "E-Commerce System",
"type": "SoftwareSystem",
"childCount": 5,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system",
"children": [
{
"id": "e-commerce-system/web-app",
"name": "Web App",
"type": "Container",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/web-app",
"children": []
},
{
"id": "e-commerce-system/api-gateway",
"name": "API Gateway",
"type": "Container",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/api-gateway",
"children": []
},
{
"id": "e-commerce-system/order-service",
"name": "Order Service",
"type": "Container",
"childCount": 2,
"viewerUrl": "/microservices-example/#/m/e-commerce-system/order-service?sel=e-commerce-system/order-service",
"children": [
{
"id": "e-commerce-system/order-service/order-controller",
"name": "Order Controller",
"type": "Component",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system/order-service?sel=e-commerce-system/order-service/order-controller",
"children": []
},
{
"id": "e-commerce-system/order-service/order-repository",
"name": "Order Repository",
"type": "Component",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system/order-service?sel=e-commerce-system/order-service/order-repository",
"children": []
}
]
},
{
"id": "e-commerce-system/inventory-service",
"name": "Inventory Service",
"type": "Container",
"childCount": 2,
"viewerUrl": "/microservices-example/#/m/e-commerce-system/inventory-service?sel=e-commerce-system/inventory-service",
"children": null
}
]
}
],
"next": null,
"truncated": true
}
Here limit=7 cut the walk after seven elements, so truncated is true.
neighbours¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"id": "e-commerce-system/web-app",
"direction": "out",
"depth": 2,
"items": [
{
"hop": 1,
"direction": "out",
"near": "e-commerce-system/web-app",
"relationship": {
"id": "e-commerce-system/web-app->e-commerce-system/api-gateway",
"source": "e-commerce-system/web-app",
"destination": "e-commerce-system/api-gateway",
"description": "Calls",
"technology": null,
"tags": [
"Relationship"
],
"properties": {}
},
"element": {
"id": "e-commerce-system/api-gateway",
"name": "API Gateway",
"type": "Container",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/api-gateway"
}
},
{
"hop": 2,
"direction": "out",
"near": "e-commerce-system/api-gateway",
"relationship": {
"id": "e-commerce-system/api-gateway->e-commerce-system/inventory-service",
"source": "e-commerce-system/api-gateway",
"destination": "e-commerce-system/inventory-service",
"description": "Routes to",
"technology": null,
"tags": [
"Relationship"
],
"properties": {}
},
"element": {
"id": "e-commerce-system/inventory-service",
"name": "Inventory Service",
"type": "Container",
"childCount": 2,
"viewerUrl": "/microservices-example/#/m/e-commerce-system/inventory-service?sel=e-commerce-system/inventory-service"
}
}
],
"next": null,
"truncated": false
}
near is the element on your side of the relationship: the element you asked about or, for a box with things inside, the thing inside it that holds the relationship.
path¶
GET /microservices-example/api/v1/path?from=e-commerce-system/web-app&to=e-commerce-system/inventory-service
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"from": "e-commerce-system/web-app",
"to": "e-commerce-system/inventory-service",
"maxHops": 6,
"hops": 2,
"chains": [
[
{
"rel": "e-commerce-system/web-app->e-commerce-system/api-gateway",
"from": "e-commerce-system/web-app",
"to": "e-commerce-system/api-gateway",
"description": "Calls",
"technology": null
},
{
"rel": "e-commerce-system/api-gateway->e-commerce-system/inventory-service",
"from": "e-commerce-system/api-gateway",
"to": "e-commerce-system/inventory-service",
"description": "Routes to",
"technology": null
}
]
],
"next": null,
"truncated": false
}
relationships¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"from": "administrator",
"to": "e-commerce-system",
"edge": null,
"total": 1,
"items": [
{
"id": "administrator->e-commerce-system/web-app",
"source": "administrator",
"destination": "e-commerce-system/web-app",
"description": "Manages",
"technology": null,
"tags": [
"Relationship"
],
"properties": {}
}
],
"next": null,
"truncated": false
}
views and view¶
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"items": [
{
"key": "system-context-view-ecommerce",
"viewKey": "system-context-view-ecommerce",
"kind": "systemContext",
"title": "System Context of E-Commerce App",
"description": "System Context of E-Commerce App",
"scope": "e-commerce-system",
"environment": null,
"viewerUrl": "/microservices-example/#/v/system-context-view-ecommerce"
},
{
"key": "deployment-view-production-ecommerce",
"viewKey": "deployment-view-production-ecommerce",
"kind": "deployment",
"title": "deployment-view-production-ecommerce",
"description": null,
"scope": null,
"environment": "production",
"viewerUrl": "/microservices-example/#/v/deployment-view-production-ecommerce"
}
],
"next": null,
"truncated": false
}
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"view": {
"key": "system-context-view-ecommerce",
"viewKey": "system-context-view-ecommerce",
"kind": "systemContext",
"title": "System Context of E-Commerce App",
"description": "System Context of E-Commerce App",
"scope": "e-commerce-system",
"environment": null,
"viewerUrl": "/microservices-example/#/v/system-context-view-ecommerce",
"elements": [
{
"id": "customer",
"name": "Customer",
"type": "Person",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m?sel=customer"
},
{
"id": "administrator",
"name": "Administrator",
"type": "Person",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m?sel=administrator"
},
{
"id": "e-commerce-system",
"name": "E-Commerce System",
"type": "SoftwareSystem",
"childCount": 5,
"viewerUrl": "/microservices-example/#/m/e-commerce-system?sel=e-commerce-system"
},
{
"id": "payment-provider",
"name": "Payment Provider",
"type": "SoftwareSystem",
"childCount": 0,
"viewerUrl": "/microservices-example/#/m?sel=payment-provider"
}
],
"relationships": [
"administrator->e-commerce-system/web-app",
"customer->e-commerce-system/web-app",
"e-commerce-system/order-service->payment-provider"
]
}
}
relationships in view is null when the view’s drawing cannot be read.
Custom elements¶
Custom elements (type=Element) answer every element endpoint like the C4 elements; they appear in elements, tree (as top-level elements), neighbours and relationships, and their relationships to C4 elements count as any other. Like a deployment element, a custom element is drawn in views only: its viewerUrl opens the first view that shows it with the element selected, and is null when no view shows it.
Deployment elements¶
Deployment nodes, infrastructure nodes and instances answer every element endpoint: elements?type=DeploymentNode (or InfrastructureNode, ContainerInstance, SoftwareSystemInstance), tree?id=deploy/production/aws, neighbours, relationships. The element answer of a deployment element has these fields as well:
environment: the key of its environment;instanceOf: for an instance, the id of the container or software system; the instance has its name, description and technology;instances: for a deployment node, how many instances it has, a number ("2") or a range ("0..N");healthChecks: for an instance, when the model has health checks.
Its viewerUrl opens the first deployment view that shows it, with the element selected; it is null when no view shows it.
{
"apiVersion": "1",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6",
"element": {
"id": "deploy/production/aws/ecs-cluster/web-app",
"modelId": "28",
"type": "ContainerInstance",
"parent": "deploy/production/aws/ecs-cluster",
"children": [],
"name": "Web App",
"description": null,
"technology": "React",
"tags": [
"Container Instance"
],
"properties": {},
"url": null,
"group": null,
"views": [
"deployment-view-production-ecommerce"
],
"childCount": 0,
"relationships": {
"own": {
"in": 0,
"out": 0
},
"withDescendants": {
"in": 0,
"out": 0
}
},
"viewerUrl": "/microservices-example/#/v/deployment-view-production-ecommerce?sel=deploy/production/aws/ecs-cluster/web-app",
"environment": "production",
"instanceOf": "e-commerce-system/web-app"
}
}
The relationships of an instance are the ones written on it, such as a load balancer that forwards requests to it. The relationships it gets from its container (buildzr copies them to the instances) are not repeated: an arrow between two instances in a deployment view stands for the container relationship, and view lists that relationship’s id.
Common questions and the request that answers each¶
| # | Question | Call |
|---|---|---|
| 1 | What does the model contain at the top level? | GET api/v1/ (counts and topLevel), then GET api/v1/tree |
| 2 | What is the e-commerce system made of? | GET api/v1/tree?id=e-commerce-system&depth=2 |
| 3 | What does the web app depend on, directly and through other things? | GET api/v1/neighbours?id=e-commerce-system/web-app&direction=out&depth=3 |
| 4 | Which elements talk to the database? | GET api/v1/neighbours?id=e-commerce-system/database&direction=in |
| 5 | How does the web app reach the inventory service (shortest chain)? | GET api/v1/path?from=e-commerce-system/web-app&to=e-commerce-system/inventory-service |
| 6 | What are the explicit relationships behind the arrow between two boxes? | GET api/v1/relationships?from=administrator&to=e-commerce-system |
| 7 | Which elements are tagged Person? | GET api/v1/elements?tag=Person |
| 8 | Where is this element shown (which views)? | GET api/v1/element?id=customer (field views) |
| 9 | Give me a link a person can open for this element. | GET api/v1/element?id=… (field viewerUrl) |
| 10 | Has the model changed since I last read it? | GET api/v1/ with If-None-Match: "<old modelHash>": 304 if not |
| 11 | Which people use the e-commerce system? | GET api/v1/neighbours?id=e-commerce-system&direction=in&type=Person |
| 12 | What uses MongoDB? | GET api/v1/elements?q=mongodb (matches technology) |
| 13 | What does this element do, what is it built with, who owns it? | GET api/v1/element?id=e-commerce-system/order-service (description, technology, properties) |
| 14 | Which authored views exist, and what does one show? | GET api/v1/views, then GET api/v1/view?key=system-context-view-ecommerce |
| 15 | What is this arrow I was linked to (sel=a->b)? |
GET api/v1/relationships?edge=customer-%3Ee-commerce-system |
| 16 | Where does the order service run in production? | GET api/v1/elements?q=order%20service&type=ContainerInstance (field parent), then GET api/v1/views?environment=production |
Question 10 in practice: send the ETag you got last time.
GET /microservices-example/api/v1/
If-None-Match: "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6"
HTTP/1.0 304 Not Modified
ETag: "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6"
Cache-Control: no-cache
Question 15: a viewer link such as #/m/e-commerce-system?sel=customer->e-commerce-system names an arrow. Pass the arrow id (customer->e-commerce-system, percent-encoded as customer-%3Ee-commerce-system) as edge. Arrows drawn to the edge of a box that continue outside it (<child>->port:<outer arrow>) work too. An explicit relationship id with a description (a->b:pays) names exactly that relationship.
Limits and paging¶
| Parameter | Smallest | Default | Largest |
|---|---|---|---|
limit of elements and relationships |
1 | 50 | 200 |
limit of neighbours |
1 | 200 | 1000 |
limit of tree |
1 | 500 | 1000 |
limit of path (chains) |
1 | 3 | 5 |
depth |
1 | 1 | 4 |
maxHops |
1 | 6 | 8 |
q |
200 characters | ||
| any other text parameter | 500 characters |
Every list says whether it was cut short. If it was, truncated is true, and for elements and relationships, next is the URL of the next page. Follow next until truncated is false.
A cursor belongs to one model. After a rebuild with a different model, an old cursor gets 409 (cursor-stale), so a reader never mixes two models. One answer never holds more than about 1 MB of items; if it would, the page ends early and next continues from there.
Errors¶
Errors are RFC 9457 problem details with the content type application/problem+json. The members type, title, status, detail and instance are always there; code is a short stable name and modelHash is added when the model could be read. type is a urn:buildzr:problem:<code> name, not a web page.
| Status | code |
When |
|---|---|---|
| 400 | bad-parameter |
A missing, unknown, repeated or malformed parameter, a control character, or text that is too long. |
| 400 | limit-exceeded |
A number outside its range. detail names the parameter and the range; minimum and maximum give it as numbers. |
| 404 | element-not-found, view-not-found |
No such element or view. candidates lists up to 5 ids that are close (“did you mean”); for elements nearest is the closest existing ancestor. |
| 404 | unknown-endpoint |
Anything else below api/v1/. |
| 405 | method-not-allowed |
Any method but GET and HEAD. The answer has Allow: GET, HEAD. |
| 409 | cursor-stale |
A cursor from another model. |
| 503 | model-unavailable |
The workspace has no readable model (for example while it is being rebuilt). Try again. |
| 503 | workspace-broken |
Building the workspace failed at the model or layout step, so it has no data. stage names the step and errors lists the first 10 errors. Fix the model and build again. |
A broken workspace without data answers every request below api/v1/, and llms.txt, like this (the Infra workspace of Serve your workspaces):
HTTP/1.0 503
Content-Type: application/problem+json
{"type": "urn:buildzr:problem:workspace-broken", "title": "Workspace broken", "status": 503,
"detail": "building this workspace failed at the model step, so it has no model data: NameError: name 'databse' is not defined",
"instance": "/infra/api/v1/", "code": "workspace-broken", "stage": "model",
"errors": ["NameError: name 'databse' is not defined", "platform.py, line 13: cluster >> \"Stores data in\" >> databse"]}
GET /microservices-example/api/v1/element?id=e-commerce-system/web-ap
HTTP/1.0 404
Content-Type: application/problem+json
{
"type": "urn:buildzr:problem:element-not-found",
"title": "Element not found",
"status": 404,
"detail": "No element 'e-commerce-system/web-ap'. Nearest existing ancestor: 'e-commerce-system'. Did you mean: 'e-commerce-system/web-app', 'e-commerce-system', 'e-commerce-system/database', 'e-commerce-system/api-gateway', 'e-commerce-system/order-service'?",
"instance": "/microservices-example/api/v1/element?id=e-commerce-system/web-ap",
"code": "element-not-found",
"parameter": "id",
"candidates": [
"e-commerce-system/web-app",
"e-commerce-system",
"e-commerce-system/database",
"e-commerce-system/api-gateway",
"e-commerce-system/order-service"
],
"nearest": "e-commerce-system",
"modelHash": "sha256:1e2b294431af70f6e5e43dbc369f543289ebdcc373d65f9b4fdeb4fcb3c53df6"
}
Paths in answers are always model ids, never file system paths.
Caching¶
Every successful answer has a strong ETag header, which is the quoted modelHash, and Cache-Control: no-cache: a client must always ask buildzr serve again, as for the other files of the workspace. Send the tag back in If-None-Match; if the model has not changed, the answer is 304 Not Modified with no body.
The tag identifies the model, not the request, so use it only for the URL you got it from. Errors have no ETag. When the workspace is rebuilt, buildzr serve notices the new data/index.json on the next request and serves the new model without a restart.
Browsers and CORS¶
By default, the API sends no CORS (Cross-Origin Resource Sharing) headers. A web page from another address therefore cannot read the API through the browser of a visitor. This protects a model inside a company network from any page that a member of staff happens to open.
To allow one web tool, name its origin (scheme, host and port, no path) with --cors-origin, once per origin:
Only a request whose Origin header is in the list gets Access-Control-Allow-Origin: <that origin> and Vary: Origin. There is no wildcard, and OPTIONS requests are not handled, so only “simple” GET requests work from a browser.
Links to the viewer: --public-url¶
Every element in an answer has a viewerUrl, a link a person can open to see that element. The link follows the viewer’s hash routes: an element with things inside opens expanded, and a leaf opens inside its parent with itself selected. Element types the viewer does not draw have viewerUrl: null.
Without --public-url, the links are paths on the same host (/microservices-example/#/m/e-commerce-system?sel=e-commerce-system/web-app). That is right when readers open the workspace at the address of buildzr serve itself. Behind a proxy, tell serve the address that readers use:
The links, endpoints and next are then absolute: https://models.example.com/architecture/microservices-example/#/m/e-commerce-system?sel=.... buildzr serve never guesses the address from request headers. A trailing / is dropped. The value must start with http:// or https:// and must not contain ? or #.
A data-only workspace (built with viewer: false or --no-viewer) has no viewer, but buildzr serve still serves it and the API works. Its viewerUrl links are null, and notes in the orientation document says so. This is also true with --public-url, because the address is used only for the links of a workspace that has a viewer.
llms.txt¶
GET /<name>/llms.txt returns a short plain-text page (under 2 KB) made from the orientation document of the workspace. It says what the workspace is and links to its api/v1/ and to every endpoint. AI tools that look for a file called llms.txt find the API through it.
What the API will not do¶
- It never writes. Other methods get
405, andbuildzr servedoes not create or change files. - It reads only the model data file and the scene files that
data/index.jsonof the workspace names, and only inside thedata/folder of the workspace. If a name inindex.jsonpoints elsewhere, the model becomes unavailable; the file is not read. - The drawing’s geometry is not in the answers. Neither is a comparison between two builds.
- Version: all paths are below
api/v1/. New fields may appear in answers without a version change; a change that removes or renames something would arrive asapi/v2/.