Context Compiler
Query your compiled context graph from your own systems: plain-language questions, the graph's records as REST APIs, an MCP server for AI agents, and a push API that keeps it current.
When to use this API
Use the Context Compiler's APIs when your applications, analysts or AI agents need answers from one governed view of your enterprise data, instead of querying each source system on its own.
In the console, the Context Compiler connects your systems, files and databases, resolves the same customer, supplier, asset or employee across them, reads the data in your domain's terms and compiles one context graph. To serve a graph to your systems, open it and choose Data Lens for your website: the platform makes the graph's Data Lens (a deployed, versioned view of the graph), deploys it and gives you its id and an API key. Every API on this page is called on that Data Lens.
| Interface | Use it for |
|---|---|
| Natural Language Query | A question in plain words, answered from the graph with the records behind it and how the answer was worked out. |
| Model APIs | The graph's entities as REST resources: list, filter and page through records, or read one with its related records. |
| MCP server | The graph as tools for an AI agent: find, get, aggregate, traverse relationships, define a term, ask. |
| Push API | Your systems send changed records in, so the graph stays current between scheduled reads. |
| Website chat | A chat for your own site or intranet, answering from the graph. |
Authentication
Every request carries the Data Lens's API key. You get one when you choose Data Lens for your
website, and you can issue more, rotate or revoke them in the console. A key opens this one Data
Lens only, is shown once (only a hash of it is kept) and can be set to expire. Keys start with
ok_live_.
x-ok-api-key: ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
x-ok-api-key: ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Parameters
Path
| Name | Type | Required | Description |
|---|---|---|---|
| lensId | string | required | The Data Lens's id, shown with its key (for example uc-8d41b2). |
Body of POST /ask
| Name | Type | Required | Description |
|---|---|---|---|
| question | string | required | The question, in plain words: "How many sites have an active critical alarm?", "Top 5 suppliers by late deliveries this quarter". |
| mode | string | optional | analytical or deterministic. Leave it out to let the platform choose. |
Request
curl -X POST https://okp.openknowra.ai/workstudio/api/v2/published/$LENS_ID/ask \
-H "x-ok-api-key: $OPENKNOWRA_LENS_KEY" \
-H "Content-Type: application/json" \
-d '{"question":"How many sites have an active critical alarm?"}'import os, json, urllib.request
url = "https://okp.openknowra.ai/workstudio/api/v2/published/" + os.environ["LENS_ID"] + "/ask"
req = urllib.request.Request(
url,
data=json.dumps({"question": "How many sites have an active critical alarm?"}).encode(),
headers={"x-ok-api-key": os.environ["OPENKNOWRA_LENS_KEY"], "Content-Type": "application/json"},
method="POST")
print(json.load(urllib.request.urlopen(req, timeout=60)))const url = `https://okp.openknowra.ai/workstudio/api/v2/published/${process.env.LENS_ID}/ask`;
const res = await fetch(url, {
method: "POST",
headers: { "x-ok-api-key": process.env.OPENKNOWRA_LENS_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({ question: "How many sites have an active critical alarm?" }),
});
console.log(await res.json());Response
200 OK - the answer, the records behind it, and how it was reached.
{
"kind": "resolved",
"found": true,
"question": "How many sites have an active critical alarm?",
"headline": "14 sites have an active critical alarm.",
"value": 14,
"count": 14,
"reading": "Sites linked to an Alarm whose severity is Critical and status is Active",
"items": [
{ "siteId": "S-0042", "name": "North Ridge", "region": "West" }
],
"truncated": false,
"grounded": { "version": 2, "publishedAt": "2026-10-12T10:20:00.000Z" },
"steps": ["Sites", "linked Alarms", "severity = Critical", "status = Active"]
}
| Field | Type | Description |
|---|---|---|
| kind | string | resolved (a direct answer), analysis (a breakdown, ranking or trend) or clarify (the question could mean more than one thing). |
| headline | string | The answer in one sentence. |
| value · count | number | The figure asked for, and how many records it covers. |
| reading | string | How the question was read, in the graph's terms. |
| items | array | The records behind the answer (truncated is true when there were more). |
| grounded | object | The Data Lens version the answer came from. |
| options | array | On a clarify answer: the readings to choose from, each with the question to ask ({ label, ask }). |
Model APIs
Every entity of the graph you confirm in the console is a REST resource, named in plural lower case
(sites, alarms, customers). Paths are relative to
https://okp.openknowra.ai/workstudio/api/v2/published/{lensId}.
| Method | Path | What it does |
|---|---|---|
| GET | /model | The APIs this version serves: each one's path, key, record count, fields with their types, and links to related APIs. |
| GET | /model/{api} | A page of records with their related records. Query: limit (1-500, default 50), offset, and any field =value as an exact-match filter (?region=West). |
| GET | /model/{api}/{key} | One record by its key, with its related records. |
curl "https://okp.openknowra.ai/workstudio/api/v2/published/$LENS_ID/model/sites?region=West&limit=20" \
-H "x-ok-api-key: $OPENKNOWRA_LENS_KEY"
{
"api": "sites",
"version": 2,
"ok": true,
"total": 38,
"count": 20,
"offset": 0,
"next": 20,
"records": [
{ "key": "S-0042", "name": "North Ridge", "region": "West",
"alarms": { "count": 3, "items": [{ "key": "AL-9910", "severity": "Critical" }] } }
]
}
next is the offset of the next page, or null on the last
one. Filters work on the fields an API returns; a sensitive field cannot be filtered on.
MCP server
The Data Lens is an MCP server (Streamable HTTP, JSON-RPC 2.0) at
https://okp.openknowra.ai/api/okp/mcp/{lensId}, so an AI agent can work with the graph
directly. Every tool is read-only.
{
"mcpServers": {
"network-graph": {
"type": "http",
"url": "https://okp.openknowra.ai/api/okp/mcp/<lensId>",
"headers": { "Authorization": "Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
| Tool | What it does |
|---|---|
| describe_data | The entities, their fields and how they relate. |
| define_term | What a business term means in this graph. |
| ask | A question in plain words, answered from the deployed version. |
| find_<entity> | Records of an entity, with filters, sort, limit (1-100), offset and fields. |
| get_<entity> | One record by its key. |
| aggregate_<entity> | count, sum, avg, min, max or count_distinct, grouped by a field or by day, week, month, quarter or year. |
| traverse · follow_relationship | Walk relationships from a record or an entity, with filters and measures along the path. Present when the graph has relationships. |
Filters are a list of { field, op, value } with op one of eq,
ne, gt, gte, lt, lte, in,
not_in, contains, starts_with, between,
exists, empty; a field can be a related path such as
"HAS_SUPPLIER.country". The server also offers the graph's dictionary, skills and taxonomy as
resources (okp://{lensId}/dictionary, /skills, /taxonomy).
Push API
Your systems can send changed records into one of the graph's connections as they happen. Switch push on for the connection's object in the console (it must have been read once); the console shows its URL and a signing secret.
| Body field | Type | Description |
|---|---|---|
| records | array | The records, in the object's own field names; up to 5,000 per call. A bare array is accepted too. |
| deleted | array | Optional: the ids of records deleted in your system. |
| snapshot | boolean | Optional: true when records is the complete set, so records missing from it are removed. |
Authenticate with the API key, or sign the request with the push secret (whsec_...):
import crypto from "node:crypto";
const body = JSON.stringify({ records: [{ siteId: "S-0042", status: "Maintenance" }] });
const ts = Math.floor(Date.now() / 1000).toString();
const sig = "sha256=" + crypto.createHmac("sha256", process.env.PUSH_SECRET).update(ts + "." + body).digest("hex");
await fetch(pushUrl, {
method: "POST",
headers: { "Content-Type": "application/json", "x-ok-timestamp": ts, "x-ok-signature": sig },
body,
});
{ "ok": true, "object": "sites", "entity": "Site", "via": "signed",
"received": 1, "inserted": 0, "updated": 1, "unchanged": 0, "deleted": 0, "skipped": 0 }
x-ok-timestamp is in seconds since the epoch and must be within five
minutes of the server's clock; the signature is the hex HMAC-SHA256 of timestamp + "." + body.
Website chat
Data Lens for your website also gives you a script tag: paste it into any page and a chat
answers from the graph. Optional attributes: data-title, data-color,
data-position, data-open, data-inline, data-theme.
<script src="https://okp.openknowra.ai/okplatform/widget.js"
data-agent="<lensId>" data-key="ok_live_xxxxxxxxxxxxxxxxxxxxxxxx"></script>
Pricing
Two things are billed, independently, on graduated slabs (each slab's rate applies only to the volume inside it):
| What | How it is counted |
|---|---|
| Context graph | The graph's nodes are counted every hour; each UTC day is charged once on that day's average, rounded up to the nearest 1,000 nodes, priced per 1,000 nodes per day. |
| Natural Language Query | Each question to /ask or the website chat, priced by your questions so far in the calendar month (it resets at 00:00 UTC on the 1st). A question is paid before it is answered and refunded if the answer fails. |
The Model APIs, the MCP server's record tools and the Push API carry no per-call charge. Current rates and an estimator: Context Compiler pricing.
Errors
Errors are { "error": "<code>", "message": "..." }; the MCP server answers with JSON-RPC errors instead.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_limit · bad_offset · bad_filter · protected_field | Model APIs: a paging value out of range, a filter on a field the API does not return, or on a sensitive field. |
| 400 | not_published | The Data Lens has no deployed version. |
| 401 | invalid_api_key · unauthorized | Missing, revoked or expired key, a key for another Data Lens, or (push) a missing, stale or wrong signature. |
| 402 | insufficient_credits | Your organisation's balance cannot pay for the question. The body carries required, balance and billingUrl. |
| 403 | push_off | Push is not switched on for this object. |
| 404 | not_found | No such Data Lens, API or record. |
| 409 | not_read | Push: the object has not been read once yet. |
| 413 | too_many | Push: more than 5,000 records in one call. |
| 423 | paused | The Data Lens is paused in the console. |
| 503 | billing_unavailable | Credits could not be checked just now; nothing was charged. Retry shortly. |
| 5xx | failed | Transient. Retry with exponential backoff. |