Skip to content

Quickstart

Five minutes. At the end you will have stored four memories and retrieved the two that matter for a specific question, with the scores that put them there.

These snippets are tested

Every request and every response body on this page is produced by docs-site/examples/verify_quickstart.py, which runs against the real application. If the API changes, that script fails before this page ships.

1. Get an API key

Sign up at app.venkai.fr, then Settings → API keys → Create. The raw key is shown once.

curl -sX POST https://api.venkai.fr/api/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"a-real-password","organization":"Acme"}' \
  -c cookies.txt

curl -sX POST https://api.venkai.fr/api/auth/api-keys \
  -H 'Content-Type: application/json' \
  -b cookies.txt \
  -d '{"label":"quickstart","environment":"production"}'

Either way you get:

{
  "id": "key_b8f08563afb5",
  "org_id": "org_1f200d429c50",
  "label": "quickstart",
  "environment": "production",
  "api_key": "vk_live_EXAMPLE_KEY_NOT_A_REAL_CREDENTIAL_0000000000"
}

The raw key is returned exactly once

Only its hash is stored. Lose it and you rotate it — you cannot read it back. Keep it out of source control; see Configuration.

2. Configure

export VENKAI_API_KEY="vk_live_…"
export VENKAI_BASE_URL="https://api.venkai.fr"

Check you can reach the service:

curl -s https://api.venkai.fr/api/health
{"status": "ok", "database": "ok"}

This is a readiness probe: it opens a database connection and runs SELECT 1. A 503 means the API is up but its store is not.

3. Create a project

You do not have to. Writing to a project key that does not exist creates it. billing-service below is a key you choose; keep it stable, because agents carry it hard-coded.

4. Store memories

curl -sX POST https://api.venkai.fr/api/memory \
  -H "Authorization: Bearer $VENKAI_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "project_id": "billing-service",
    "agent_id": "planner",
    "type": "decision",
    "importance": 0.9,
    "content": "We use Postgres, not MongoDB, because the ranking query needs joins.",
    "metadata": {"ticket": "ARCH-14"}
  }'
{
  "id": "ctx_cc55e4a7bbc5",
  "org_id": "org_1f200d429c50",
  "project_id": "billing-service",
  "agent_key": "planner",
  "type": "decision",
  "content": "We use Postgres, not MongoDB, because the ranking query needs joins.",
  "confidence": 0.8,
  "importance": 0.9,
  "access_count": 0,
  "last_accessed_at": null,
  "created_at": 1787007131.3763673,
  "updated_at": 1787007131.3763673,
  "metadata": {"ticket": "ARCH-14"},
  "embedding": [0.0, 0.0, 0.0, 0.30151134457776363, "… 128 floats in total"]
}

The write response includes the raw embedding vector

Only the write path returns it. Every read path (GET /api/context/…, …/relevant) strips it before responding. Ignore the field; it is an internal artefact and its length depends on your provider.

Add three more so ranking has something to do:

for m in \
  '{"type":"constraint","importance":0.8,"content":"The retrieval endpoint must answer in under 200 ms at p95."}' \
  '{"type":"fact","importance":0.4,"content":"The staging environment is rebuilt from scratch every Sunday night."}' \
  '{"type":"preference","importance":0.2,"content":"The team writes commit messages in English."}' ; do
  curl -sX POST https://api.venkai.fr/api/memory \
    -H "Authorization: Bearer $VENKAI_API_KEY" -H 'Content-Type: application/json' \
    -d "$(echo "$m" | python -c 'import json,sys; d=json.load(sys.stdin); d.update(project_id="billing-service", agent_id="planner"); print(json.dumps(d))')" \
    -o /dev/null -w '%{http_code}\n'
done

Valid types are fact, decision, preference, event, constraint, relationship. Anything else is a 422 — the API will not guess.

5. Retrieve the relevant context

curl -sG https://api.venkai.fr/api/context/billing-service/relevant \
  -H "Authorization: Bearer $VENKAI_API_KEY" \
  --data-urlencode 'query=which database did we pick' \
  --data-urlencode 'limit=2'
{
  "project_id": "billing-service",
  "query": "which database did we pick",
  "memories": [
    {
      "id": "ctx_cc55e4a7bbc5",
      "type": "decision",
      "content": "We use Postgres, not MongoDB, because the ranking query needs joins.",
      "importance": 0.9,
      "access_count": 0,
      "created_at": 1787007131.3763673,
      "metadata": {"ticket": "ARCH-14"},
      "score": 0.4789,
      "similarity": 0.1348,
      "justification": "semantic similarity 0.13 (HashingEmbeddingProvider, 128d)"
    },
    { "…": "second result" }
  ]
}

Four memories in, two out. That is the whole product.

Note the low similarity

0.13 for an obviously on-topic memory is not a bug — it is the default hashing provider, which measures token overlap in disguise, not meaning. The decision still won because importance (0.9) and recency carry 60 % of the score. For real semantic matching, switch the provider: Embeddings.

6. Inject it into an agent

import os, requests

BASE = os.environ["VENKAI_BASE_URL"]
H = {"Authorization": f"Bearer {os.environ['VENKAI_API_KEY']}"}

def venkai_context(project: str, task: str, limit: int = 5) -> str:
    r = requests.get(f"{BASE}/api/context/{project}/relevant",
                     headers=H, params={"query": task, "limit": limit}, timeout=10)
    r.raise_for_status()
    memories = r.json()["memories"]
    if not memories:
        return ""
    lines = [f"- [{m['type']}] {m['content']}" for m in memories]
    return "What this project already established:\n" + "\n".join(lines)

task = "add a new billing plan"
prompt = f"{venkai_context('billing-service', task)}\n\nTask: {task}"

limit is your context budget. It is a number you set, not one that grows with the age of the project.

Next