InvariantDB records, for each write, what the value is, when it was true (valid time), and when the system recorded it (recorded time). Pick the path that matches what you have:
For a longer walkthrough that covers backdated writes, agent memory, and MCP setup, see the 30-minute onboarding runbook.
InvariantDB runs a Model Context Protocol server for each graph. One configuration entry gives your agent 28 tools: Cypher queries, full-text and vector search, schema and index listing, agent memory (remember, recall, record and replay episodes), documents, belief lineage, property provenance, and compliance procedures.
In your MCP client, add a remote (HTTP) MCP server with the URL https://invariantdb.com/graphs/{name}/mcp, replacing {name} with your full graph name. Do not add an Authorization header. When the approval page opens in your browser, paste your gq_ key and approve. See Connect with OAuth sign-in.
Clients that accept an HTTP server with headers in a JSON configuration file can use this block. Replace {name} with your full graph name. The file location and field names differ between clients; check your client’s MCP documentation.
{
"mcpServers": {
"invariantdb": {
"type": "http",
"url": "https://invariantdb.com/graphs/{name}/mcp",
"headers": {
"Authorization": "Bearer gq_paste_your_api_key_here"
}
}
}
}
Restart or reload the client. The invariantdb tools appear in its tool list. The endpoint is https://invariantdb.com/graphs/{name}/mcp (HTTP POST, JSON-RPC 2.0). See the MCP tools reference.
Sign in to the dashboard and create a graph. POST /cloud/me/graphs returns the raw API key exactly once, so copy it immediately.
Example response (fictional values):
{
"graph_id": "grp_…",
"name": "a1b2c3_my-graph",
"requested_name": "my-graph",
"status": "active",
"api_key": "gq_…",
"api_key_id": "…",
"api_key_prefix": "gq_Xy12a",
"mcp_config": {
"mcpServers": {
"invariantdb": {
"type": "http",
"url": "https://…/graphs/a1b2c3_my-graph/mcp",
"headers": { "Authorization": "Bearer gq_…" }
}
}
},
"already_existed": false
}
Use the value of name in every request; it includes a tenant prefix. On repeat calls with the same graph name, the response returns "api_key": null and "mcp_config": null. If you lost the original key, create a new one in the dashboard's API keys panel.
"Show me every employee whose role changed in the last 90 days, with the timestamp the change was recorded versus when it took effect."
The agent writes Cypher, for example MATCH (p:Person) CALL db.propertyHistory(id(p), 'role') YIELD value, validFrom, txnAt RETURN p.name, value, validFrom, txnAt, and replies with the result.
Create the graph in the dashboard first and use the graph name it shows. The examples use a1b2c3_my-graph (fictional).
pip install "https://sdk.invariantdb.com/python/invariantdb-0.8.0-py3-none-any.whl#sha256=15f7ef0dde4f77cb1c3971dbdf750a9f07ef1f9733ffdc166346ae2b108b65dc"
The Python SDK is not published on PyPI. Install it from this URL; pip checks the downloaded file against the SHA-256 in the URL.
from invariantdb import Client
db = Client("https://invariantdb.com", api_key="gq_...")
graph = "a1b2c3_my-graph"
rows = db.cypher(
graph=graph,
query="MATCH (p:Person) WHERE p.role = 'engineer' RETURN p.name, p.startDate",
).rows
# Agent memory in one call. The payload is a JSON string.
db.cypher(graph=graph, query="""
CALL db.recordEpisode($s, 'observation', $text, '{"topic":"pricing"}')
""", parameters={"s": "conv-abc-123", "text": "User asked about pricing"})
npm install https://sdk.invariantdb.com/node/invariantdb-0.8.0.tgz
import { Client } from 'invariantdb';
const db = new Client({ url: 'https://invariantdb.com', apiKey: process.env.INVARIANTDB_API_KEY });
const result = await db.cypher('a1b2c3_my-graph', 'MATCH (n) RETURN count(n) AS total');
console.log(result.asDicts());
Full SDK reference: SDK reference.
Graph creation uses a JWT (from the dashboard sign-in flow). Cypher queries use the raw gq_... API key returned by that create call.
KEY="gq_paste_your_api_key_here"
JWT="paste_your_dashboard_jwt_here"
# 1. Create a graph (idempotent; returns the raw api_key once).
# The response field "name" is the full graph name.
curl -fsS -X POST -H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
--data '{"name": "my-graph"}' \
https://invariantdb.com/cloud/me/graphs
GRAPH="<name from the create response>"
# 2. Run a Cypher query
curl -fsS -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
--data '{"query": "CREATE (p:Person {name: \"Alice\", role: \"engineer\"}) RETURN p"}' \
"https://invariantdb.com/graphs/$GRAPH/cypher?format=flat"
# 3. Read it back, with each version of the node
curl -fsS -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
--data '{"query": "MATCH (p:Person {name: \"Alice\"}) CALL db.history(id(p)) YIELD version, timestamp, properties, validFrom, validTo RETURN version, timestamp, properties, validFrom, validTo"}' \
"https://invariantdb.com/graphs/$GRAPH/cypher?format=flat"
# 4. Record who made a change (optional)
curl -fsS -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
--data '{"query": "MATCH (p:Person {name: \"Alice\"}) SET p.role = \"staff engineer\"", "provenance": {"actor": "hr-sync", "reason": "promotion", "confidence": 1.0}}' \
"https://invariantdb.com/graphs/$GRAPH/cypher?format=flat"
format=flat returns plain JSON values; without it, values are tagged ({"string":"Alice"}). db.history returns timestamp, validFrom, and validTo in nanoseconds since the Unix epoch. From 1.4.2 it also returns kind and principal, the credential that wrote each version.
Read the provenance back with CALL db.propertyProvenance(id(p), 'role'). See Record who made a change for the fields and headers.
Full HTTP API: /openapi.yaml.
| If you're building… | Read |
|---|---|
| An AI agent that remembers | AI memory primitives |
| An audit trail with a hash-chained history | Compliance dashboard |
| A research data API key that returns only noised aggregates | Features › Private mode |
| Anything in healthcare / finance / insurance / public sector | Regulated industries |
| The full feature catalog | Features |
401 unauthorized — the Authorization header is missing or the key is wrong. Subcode EXPIRED_API_KEY or REVOKED_API_KEY means you need a new key.403 forbidden — the key cannot use this graph or operation. Check the subcode: CROSS_TENANT_GRAPH_MISMATCH means the graph belongs to another tenant. No subcode on a /graphs/ URL usually means the key is limited to other graphs.404 not_found — no graph with that name. Use the full name from the dashboard.408 query_timeout — narrow the query, or set the X-Query-Timeout-Ms header (milliseconds, up to 290000).409 — a node or edge with that ID already exists.413 — the request body is too large. Keep Cypher requests under 2 MiB and send large loads in batches.429 or 503 — wait for the Retry-After interval, then retry with jitter. From 1.4.2 an error body says whether to retry (retryable, retry), and a write sent with an Idempotency-Key header can be resent with the same key without being applied twice.PRIVATE_MODE_NON_AGGREGATE_RETURN — you're using a private-mode key; only expressions built on count, sum or avg may be returned. See Features › Private mode.400 cypher_parse — the response includes position with line and col. See the Cypher reference.