Skip to content

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.

GET /microservices-example/api/v1/
{
  "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, tags and topLevel describe the model; topLevel lists the ids to start from, the top-level deployment nodes included.
  • environments lists the deployment environments: key (used in deployment element ids and in views?environment=), name, the top-level deployment nodes and the deployment views.
  • endpoints are URL templates: replace the {...} parts. Parameters after ? or & inside braces are optional.
  • limits are the largest values each parameter accepts (see Limits).
  • viewerUrl is the address of the viewer. It is null when the workspace has no viewer (no index.html), and notes says so.
  • builtWith is null when the workspace has no build.json.
  • status is ok, or broken when 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 has stage (check), errors (the first 10) and a sentence in notes, 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

GET /microservices-example/api/v1/element?id=e-commerce-system/web-app
{
  "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

GET /microservices-example/api/v1/elements?q=mongo
{
  "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

GET /microservices-example/api/v1/tree?id=e-commerce-system&depth=2&limit=7
{
  "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

GET /microservices-example/api/v1/neighbours?id=e-commerce-system/web-app&direction=out&depth=2
{
  "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

GET /microservices-example/api/v1/relationships?from=administrator&to=e-commerce-system
{
  "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

GET /microservices-example/api/v1/views
{
  "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
}
GET /microservices-example/api/v1/view?key=system-context-view-ecommerce
{
  "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.

GET /microservices-example/api/v1/element?id=deploy/production/aws/ecs-cluster/web-app
{
  "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:

buildzr serve --cors-origin https://tool.example.com --cors-origin http://localhost:3000

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.

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:

buildzr serve --public-url https://models.example.com/architecture/

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, and buildzr serve does not create or change files.
  • It reads only the model data file and the scene files that data/index.json of the workspace names, and only inside the data/ folder of the workspace. If a name in index.json points 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 as api/v2/.