OpenKnowra Platform · Context Graph API

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.

POST /workstudio/api/v2/published/{lensId}/ask
Base URL https://okp.openknowra.ai Auth Data Lens API key Content-Type application/json Also as Model APIs · MCP server · Push · Website chat

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.

InterfaceUse it for
Natural Language QueryA question in plain words, answered from the graph with the records behind it and how the answer was worked out.
Model APIsThe graph's entities as REST resources: list, filter and page through records, or read one with its related records.
MCP serverThe graph as tools for an AI agent: find, get, aggregate, traverse relationships, define a term, ask.
Push APIYour systems send changed records in, so the graph stays current between scheduled reads.
Website chatA chat for your own site or intranet, answering from the graph.
Grounded and versioned. Answers come from the deployed version of the Data Lens and name it, and fields marked sensitive in the graph are masked, tokenised or left out, on every interface.

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_.

Query and Model APIs
x-ok-api-key: ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
MCP server and Push API (either header)
x-ok-api-key: ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Keep keys server-side. Call these APIs from your servers. The one exception is the website chat, whose key sits in your page by design: give it its own key, so you can rotate it on its own.

Parameters

Path

NameTypeRequiredDescription
lensIdstringrequiredThe Data Lens's id, shown with its key (for example uc-8d41b2).

Body of POST /ask

NameTypeRequiredDescription
questionstringrequiredThe question, in plain words: "How many sites have an active critical alarm?", "Top 5 suppliers by late deliveries this quarter".
modestringoptionalanalytical 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.

application/json
{
  "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"]
}
FieldTypeDescription
kindstringresolved (a direct answer), analysis (a breakdown, ranking or trend) or clarify (the question could mean more than one thing).
headlinestringThe answer in one sentence.
value · countnumberThe figure asked for, and how many records it covers.
readingstringHow the question was read, in the graph's terms.
itemsarrayThe records behind the answer (truncated is true when there were more).
groundedobjectThe Data Lens version the answer came from.
optionsarrayOn 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}.

MethodPathWhat it does
GET/modelThe 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.
Request
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"
200 OK
{
  "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" }] } }
  ]
}
Paging. 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.

MCP client configuration
{
  "mcpServers": {
    "network-graph": {
      "type": "http",
      "url": "https://okp.openknowra.ai/api/okp/mcp/<lensId>",
      "headers": { "Authorization": "Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx" }
    }
  }
}
ToolWhat it does
describe_dataThe entities, their fields and how they relate.
define_termWhat a business term means in this graph.
askA 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_relationshipWalk 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.

POST /api/okp/push/{lensId}/{connectionId}/{object}
Body fieldTypeDescription
recordsarrayThe records, in the object's own field names; up to 5,000 per call. A bare array is accepted too.
deletedarrayOptional: the ids of records deleted in your system.
snapshotbooleanOptional: 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_...):

Signed request (Node)
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,
});
200 OK
{ "ok": true, "object": "sites", "entity": "Site", "via": "signed",
  "received": 1, "inserted": 0, "updated": 1, "unchanged": 0, "deleted": 0, "skipped": 0 }
Signing. 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.

Embed
<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):

WhatHow it is counted
Context graphThe 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 QueryEach 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.

StatusCodeMeaning
400bad_limit · bad_offset · bad_filter · protected_fieldModel APIs: a paging value out of range, a filter on a field the API does not return, or on a sensitive field.
400not_publishedThe Data Lens has no deployed version.
401invalid_api_key · unauthorizedMissing, revoked or expired key, a key for another Data Lens, or (push) a missing, stale or wrong signature.
402insufficient_creditsYour organisation's balance cannot pay for the question. The body carries required, balance and billingUrl.
403push_offPush is not switched on for this object.
404not_foundNo such Data Lens, API or record.
409not_readPush: the object has not been read once yet.
413too_manyPush: more than 5,000 records in one call.
423pausedThe Data Lens is paused in the console.
503billing_unavailableCredits could not be checked just now; nothing was charged. Retry shortly.
5xxfailedTransient. Retry with exponential backoff.