Download OpenAPI specification:
Read-only Cypher queries over the instance data of a knowledge network. A query is compiled into a single SQL statement and executed through vega-backend, so the subset accepted here is what compiles that way: one MATCH over a single path, a WHERE over properties, and a projection of properties and aggregates of them. Everything else is refused by name rather than approximated. Paging is written in the query itself with SKIP and LIMIT; there is no paging parameter. Exposed under /api/bkn-backend/v1 (OAuth2), with the branch query parameter defaulting to main.
Compiles a Cypher query against the knowledge network's model and returns the rows it
produces. Requires application/json content type and the query_data operation on each
object type and relation type the query names; a network-wide query_data grant covers
them, since the child operation inherits from its parent network. Every property the
query names -- in RETURN, WHERE, ORDER BY, an aggregate or an inline property map --
must have the effective property access level full for the caller. A property at
level none does not exist for the caller: naming it is a 400
BknBackend.Cypher.InvalidQuery "unknown property", exactly as for a property outside
the model, and it is never offered as a suggestion. A Column Masking (masked) or schema-only property is
refused with 403 BknBackend.Cypher.PropertyForbidden naming it as
object_type_id.property. Join keys that a relation type maps are not checked unless
the query names them. Column Masking is not applied to Cypher results.
The caller needs no grant on the vega resources behind those object types. The compiled statement runs through vega-backend's raw query under the caller's own identity first, and vega-backend checks view_detail on each resource, as before; a caller holding those grants is answered that way. Only when vega-backend refuses that run with 403, on the main branch and when every object type and relation type binding the query reads through is a current published binding, is the same statement run again under the knowledge network's managed proxy account, which vega-backend checks for view_detail on each resource. The statement is generated from the model, so it references only the resources of those bindings. When the network has no usable proxy (no mapping, not active, not synchronized with the published model, or a binding not published), on other branches, or when the proxy account is refused too, the refusal is returned as 403 BknBackend.Cypher.Forbidden. Other failures are not retried.
A concept the caller may not query is absent from the model the query is compiled against, so naming it reads as an unknown label rather than as a refusal: this endpoint cannot be used to find out what a model contains. A caller who may read nothing in the network gets 403.
Labels and relationship types accept either the id or the name of the object type or relation type. Properties are addressed by property name; display names and logic properties are not accepted. A query that does not write its own LIMIT is capped at 1000 rows, and a LIMIT above 10000 is refused rather than silently reduced.
The generated SQL is not returned: it names physical tables and columns, which the caller is not entitled to just because they may read the data.
| kn_id required | string Business knowledge network ID |
| branch | string Default: "main" Branch name. Defaults to main. |
object Values for the $name references in the query. A parameter is a value and never an identifier: it can change which rows come back, never which resource or column is read. Strings, numbers and booleans are accepted; a null is refused, because IS NULL says that and a comparison against null never matches. A parameter used as IN $name takes a non-empty array of scalars of one kind (integers and floats both count as numbers), with no null and at most 500 elements. Several long lists in one query can still be refused with 400 as too large; split them across queries. | |
object Properties a returned node carries besides its identity, keyed by the node's variable. Each variable must be a node the query returns, and at most 128 properties may be named in all, across every variable; the properties are read under the same authorization as any other property the query names. | |
| explain | boolean Return the query's plan instead of its rows. The query is read, authorized and compiled exactly as it would be to run, so it is refused in the same cases and with the same error, but nothing is read from the data and no SQL is returned. entries is then empty and plan is set. One exception: explain does not see where the data lives, so a form refused only on datasets, indexes or across data sources (see the query description) passes explain and is refused when run. |
| timeout_sec | integer >= 1 How long the query may run at the data source, in seconds. Every statement it runs (the parts of a UNION, or a bind-join and its SQL fallback) shares this one limit. Leave it out to use the service's configured timeout (55 seconds unless the deployment sets BKN_CYPHER_QUERY_TIMEOUT_SEC). It can only shorten that timeout: a value above it, or below 1, is refused with BknBackend.Cypher.InvalidParameter. |
| query required | string <= 8192 characters The Cypher query. Supported shape:
MATCH (a:Label [{property: value, ...}]) [-[:RELATION]-(b:Label) ...]
[WHERE or, to filter after aggregating, with WITHs after every MATCH:
MATCH ... [WHERE or, to filter by the values of an earlier stage (a semi-join): MATCH ... [WHERE ...] WITH [DISTINCT] a.property AS v | collect(a.property) AS s MATCH ... WHERE b.property = v | b.property IN s [AND ...] RETURN ... or, to pass nodes on to the next MATCH:
MATCH ... [WHERE ...] WITH a, ..., aggregate(...) AS name, ... [WHERE ...]
MATCH (a)-[...]-(b...) [WHERE or, to compare two collected lists: MATCH ... [WHERE ...] WITH collect([DISTINCT] a.property) AS l1 MATCH ... [WHERE ...] WITH l1, collect([DISTINCT] b.property) AS l2 RETURN size([x IN l1 WHERE x IN l2]) | size(l1) | size(l2) [AS alias], ... Each form may open with WITH A condition compares a property with =, <>, <, >, <= or >= against a literal, a parameter or another property (o.shipped < o.ordered, a.code = b.code; properties the model types as a string and a number cannot be compared), or tests it with IN [ ... ], IN $list (the whole list as one parameter), IS NULL or IS NOT NULL. A string property can also be matched with STARTS WITH, ENDS WITH or CONTAINS against a string literal or parameter: the match is a literal substring match (% and _ are not wildcards), a null property never matches, case sensitivity follows the column collation, and using them on a non-string property is refused with 400. Conditions combine with AND, OR and NOT, and parentheses group them. XOR is not supported. Not-equal is written <>; != is read as <> too. A date or time property can be compared with date('2026-08-13'), datetime(...) or localdatetime(...) over a string literal or parameter; functions other than these, coalesce, size (of a list a WITH collected) and the aggregates, and date() with no argument, are refused. CASE and coalesce compute a value wherever arithmetic may stand: in RETURN, WITH,
ORDER BY, either side of a comparison and the argument of an aggregate, as in
RETURN can also return a condition, written as in WHERE, as in
A WITH groups by what it passes on that is not an aggregate; a node groups by its primary key, so two objects that share a name stay two groups. Values in WITH are named with AS. Its WHERE compares those names, properties of the nodes it passed on, values computed from them, literals and parameters: a condition on grouping keys filters rows before grouping, and one on aggregates is tested per group (SQL HAVING). A list a WITH collected is read only as size(list), the count of what it collected. Further WITHs pass on names and values computed from them, each with its own WHERE; only the first may aggregate. RETURN lists the names, properties of the nodes and values computed from them; after a WITH that does not aggregate, count() may follow. After a WITH that aggregates, RETURN may instead aggregate its groups, as in RETURN count() AS groups: then it holds only such aggregates and returns one row. Refused: MATCH after a second WITH, a WITH after the second MATCH other than to compare two lists, WITH DISTINCT, WITH *, ORDER BY / SKIP / LIMIT on WITH, IN / string predicates in its WHERE, and aggregating in a WITH after the first. Object types bound to a resource outside a SQL data source (a dataset or an index), or read from more than one data source, are read through the resource data API and joined instead of as one SQL statement. There only the basic forms run: CASE, coalesce, totals over groups, comparing two lists, passing nodes on and collect are refused with 400, and a read too large or too slow is refused too. Nodes, relationships and derived relationships are returned as on a SQL data source, and UNION works, each part read on its own. In the semi-join form each row of the second stage is kept once if its value is among the first stage's, however often the first stage produced it; v may also be an inline property value ({property: v}). The first stage's properties and rows are authorized like any other part of the query. The WITH holds one item; without DISTINCT, RETURN must be DISTINCT and not aggregate. v is used only as = and s only as IN, each as a condition of its own at the top of WHERE (not under OR or NOT). Passing nodes on joins each row the WITH passed on to the rows the next MATCH finds for its nodes, matched up by primary key; the nodes may be written there without their labels. The WITH's named values may be returned, sorted by and computed with; when the query aggregates, those read in an expression or ORDER BY must also be returned on their own, as grouping keys. They cannot be tested in the next MATCH's WHERE (test them in the WITH's WHERE). Every node passed on must be used in the next MATCH, nodes cannot be renamed or passed beside a collected list, the next MATCH cannot be OPTIONAL, and that WITH takes no DISTINCT, ORDER BY, SKIP or LIMIT. After any WITH, RETURN may return the nodes it passed on. Such a query always runs on the SQL path. Comparing two lists returns one row of sizes: of each list, and of the elements of one list the other also holds, size([x IN l1 WHERE x IN l2]). A list collected without DISTINCT keeps its repeats, and the shared count counts the filtered list's repeats, as Cypher does; counting both ways round needs DISTINCT on both lists. When the second stage matches nothing there is no row. Neither WITH takes a WHERE, the second MATCH may not use the first list, the lists themselves cannot be returned (to list shared values, share a node in one MATCH), and the two properties must not be a string and a number. UNWIND is refused. Such a query always runs on the SQL path. RETURN can return a node or a relationship itself. A node is its object type and its full primary key, each key value as exact text with its declared type, plus the properties object_properties names; a relationship is its relation type, the identities of its two ends and the key pairs that joined them. collect(x) gathers x over each group into a list, keeping duplicates and skipping null; {k: x} builds a map. Nodes and relationships group by identity, so RETURN t, count(*) counts per object. Such a query runs as one SQL statement, whatever executor is configured. With collect, each result row is a conclusion, the values beside collect, and LIMIT counts conclusions: every matched row of each conclusion it keeps is collected. The answer is complete or refused: evidence reaching 10000 rows fails with 400 BknBackend.Cypher.ReadBudgetExceeded rather than returning part of it. Graph values cannot be sorted or made DISTINCT, collect cannot be mixed with other aggregates, beside collect only nodes, relationships, properties and constants may be returned and sorting is by the alias of a returned property, and relationships have no properties of their own (r.x is refused). Aggregates are count, sum, avg, min and max, each also in its DISTINCT form, plus count(*). count(n) counts the rows where node n was found, and count(DISTINCT n) counts distinct objects by their primary key, by all its properties together when it has several (an object type without one is refused). Cypher has no GROUP BY, and writing one is a syntax error that says so: aggregating anything groups by everything else the query returns. A grouped or DISTINCT result can only be sorted by a value it returns. A MATCH may contain several comma-separated paths, and a query may contain several MATCH clauses; both mean one shape. A variable written twice is the node it named the first time, which is how two paths meet, and paths that share no variable combine pairwise. Two parts that are neither connected nor narrowed by any condition (every row of one table against every row of another) are refused with 400 Unsupported. The first mention of a node names its object type; a later one may omit the label but must not contradict it. A path may contain several relationships. Each names exactly one relation type and may be written -[:R]->, <-[:R]- or undirected as -[:R]-; each node names exactly one object type and may carry an inline property map, which means the equalities it stands for. Cypher forbids one MATCH from traversing the same relationship twice, and that
is enforced: within one MATCH, every pair of hops over one relation type must
differ in its source row or its target row, which needs those object types to
have a primary key. Where one of the hops is undirected the requirement cannot
be stated, and the query is refused rather than answered with paths Cypher would
not return. Hops in different MATCH clauses are not compared: writing
|
{- "parameters": {
- "region": "eu",
- "floor": 100
}, - "object_properties": {
- "t": [
- "team_name"
]
}, - "explain": true,
- "timeout_sec": 1,
- "query": "MATCH (o:order)-[:rel_order_item_order]->(i:order_item) WHERE o.status IN ['paid', 'shipped'] AND o.amount > $floor RETURN o.status AS status, count(*) AS orders ORDER BY orders DESC LIMIT 20"
}{- "columns": [
- {
- "name": "string",
- "type": "string"
}
], - "entries": [
- { }
], - "value_types": {
- "property1": {
- "kind": "string",
- "object_type_id": "string",
- "relation_type_id": "string",
- "fields": {
- "property1": { },
- "property2": { }
}, - "element": { }
}, - "property2": {
- "kind": "string",
- "object_type_id": "string",
- "relation_type_id": "string",
- "fields": {
- "property1": { },
- "property2": { }
}, - "element": { }
}
}, - "execution": {
- "match_rows": 0,
- "match_complete": true,
- "conclusions_limited": true
}, - "plan": {
- "shape": "match",
- "graph": true,
- "objects": [
- {
- "alias": "string",
- "object_type_id": "string"
}
], - "relations": [
- {
- "relation_type_id": "string",
- "source": "string",
- "target": "string",
- "direction": "string"
}
], - "conditions": [
- {
- "operator": "string",
- "expression": "string",
- "properties": [
- "string"
], - "where": "string"
}
], - "grouping": [
- "string"
], - "columns": [
- {
- "name": "string",
- "kind": "string",
- "type": {
- "kind": "string",
- "object_type_id": "string",
- "relation_type_id": "string",
- "fields": {
- "property1": { },
- "property2": { }
}, - "element": { }
}
}
], - "ordering": [
- {
- "column": "string",
- "property": "string",
- "descending": true
}
], - "limit": 0,
- "limit_source": "explicit",
- "reads": [
- "string"
], - "row_filtered": [
- "string"
], - "warnings": [
- "string"
], - "warning_codes": [
- "disconnected_parts"
]
}, - "limited": true
}