Quick Start
This guide takes you from nothing to a running Dakera v0.12.3 server with persistent agent memory, in about five minutes. You start the server with Docker, store three memories, recall them with a natural-language question, and group memories into a session, using curl or the Python, TypeScript, Go or Rust SDK.
It is written for developers trying Dakera for the first time. Upgrading an existing v0.11.108 deployment? Read Upgrading to v0.12 instead.
Watch it run first: below is a real session of the five steps on this page, recorded on a clean machine with the same commands and values. Long waits are shortened and marked with their real length.
dakera.ai/playground lets you store memories, run semantic recall, explore the knowledge graph, and compare LLM memory quality right in the browser. Use it to get a feel for the API before spinning up your own server.
- Docker installed and running
curlor a REST client (for testing without an SDK)- Python 3.10+, Node.js 20.12+, Go 1.21+ or a current stable Rust toolchain (depending on your SDK choice)
- ~5 minutes to complete
Run the server
Pull and start the v0.12.3 image from GitHub Container Registry. Data is kept on the dakera-data volume:
docker run -d \
--name dakera \
--restart unless-stopped \
-p 3000:3000 \
-e DAKERA_ROOT_API_KEY=my-dev-key \
-e DAKERA_STORAGE=filesystem \
-v dakera-data:/data \
-v dakera-models:/app/models \
ghcr.io/dakera-ai/dakera:0.12.3
DAKERA_ROOT_API_KEY is required while authentication is on (the default): with no key configured the server logs an error and exits. Set DAKERA_AUTH_ENABLED=false only for local development. Without DAKERA_STORAGE=filesystem the server keeps data in memory only (the default storage is memory). The dakera-models volume (/app/models) holds models the image does not ship and is optional with the default models.
Verify the server is running:
curl http://localhost:3000/health/ready # 200 once the models are loaded
curl http://localhost:3000/health
The image ships its default models, so a cold start is ready in a few seconds (4.4 s measured) without network access. While models load, /health and /health/ready answer 503 with progress (/health/live answers 200); once loaded, /health reports the version and any config_warnings. Upgrading from v0.11? See Upgrading to v0.12.
Expected response:
{
"service": "dakera",
"status": "healthy",
"version": "0.12.3",
"unreadable_records": 0,
"corrupt_records": 0,
"degraded": [],
"config_warnings": []
}
Docker images add a build_sha field. status is "degraded" when a component is running without something it should have (see degraded); config_warnings lists startup configuration the server accepted although it is not what it should be. For a readiness probe use GET /health/ready (200 once the embedding model is loaded and storage answers); for liveness use GET /health/live.
Use your server's public IP instead of localhost if running remotely. See Deployment for Compose, K8s, and Helm.
Troubleshooting
Port 3000 already in use — Change the host port: -p 3301:3000 and use :3301 in subsequent commands.
Cannot connect to Docker daemon — Start Docker Desktop (macOS/Windows) or run sudo systemctl start docker (Linux).
Connection refused on health check — The container is still starting. The REST port binds before the models load, so once it accepts connections /health/live answers 200 while /health/ready answers 503 with the phase and download progress; wait for /health/ready to answer 200 before the first request (other routes answer 503 + Retry-After: 5 meanwhile). Check container logs with docker logs dakera.
Install an SDK
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install dakeranpm install @dakera-ai/dakera
# yarn add @dakera-ai/dakera | pnpm add @dakera-ai/dakerago get github.com/dakera-ai/dakera-go# Cargo.toml
[dependencies]
dakera-client = "0.12"
tokio = { version = "1", features = ["full"] }brew install dakera-ai/tap/dk # or see the CLI page for apt, dnf and install scripts
export DAKERA_API_KEY=my-dev-key
dk init # interactive setup: server URL and default namespaceStore memories
Store three memories to see how semantic search works. Set your server URL and API key, then store each one:
# Memory 1: UI preference
curl -X POST http://localhost:3000/v1/memory/store \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"content": "User prefers dark mode and minimal UI animations",
"importance": 0.8,
"tags": ["preference", "ui"]
}'
# Memory 2: Team process
curl -X POST http://localhost:3000/v1/memory/store \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"content": "Team standup is every morning at 9:30 AM in the #daily channel",
"importance": 0.7,
"tags": ["team", "process"]
}'
# Memory 3: Infrastructure
curl -X POST http://localhost:3000/v1/memory/store \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"content": "The deployment pipeline uses GitHub Actions with a staging environment on port 8080",
"importance": 0.9,
"tags": ["infra", "deployment"]
}'import os
from dakera import DakeraClient
# export DAKERA_API_URL=http://<YOUR_SERVER_IP>:3000
# export DAKERA_API_KEY=my-dev-key
client = DakeraClient(
base_url=os.getenv("DAKERA_API_URL", "http://localhost:3000"),
api_key=os.getenv("DAKERA_API_KEY")
)
# Memory 1: UI preference
client.store_memory(
agent_id="my-agent",
content="User prefers dark mode and minimal UI animations",
importance=0.8,
tags=["preference", "ui"]
)
# Memory 2: Team process
client.store_memory(
agent_id="my-agent",
content="Team standup is every morning at 9:30 AM in the #daily channel",
importance=0.7,
tags=["team", "process"]
)
# Memory 3: Infrastructure
client.store_memory(
agent_id="my-agent",
content="The deployment pipeline uses GitHub Actions with a staging environment on port 8080",
importance=0.9,
tags=["infra", "deployment"]
)import { DakeraClient } from '@dakera-ai/dakera';
const client = new DakeraClient({
baseUrl: process.env.DAKERA_API_URL ?? 'http://localhost:3000',
apiKey: process.env.DAKERA_API_KEY,
});
// Memory 1: UI preference
await client.storeMemory('my-agent', {
content: 'User prefers dark mode and minimal UI animations',
importance: 0.8,
tags: ['preference', 'ui'],
});
// Memory 2: Team process
await client.storeMemory('my-agent', {
content: 'Team standup is every morning at 9:30 AM in the #daily channel',
importance: 0.7,
tags: ['team', 'process'],
});
// Memory 3: Infrastructure
await client.storeMemory('my-agent', {
content: 'The deployment pipeline uses GitHub Actions with a staging environment on port 8080',
importance: 0.9,
tags: ['infra', 'deployment'],
});import (
"context"
"fmt"
"os"
dakera "github.com/dakera-ai/dakera-go"
)
ctx := context.Background()
serverURL := os.Getenv("DAKERA_API_URL")
if serverURL == "" { serverURL = "http://localhost:3000" }
client := dakera.NewClientWithOptions(dakera.ClientOptions{
BaseURL: serverURL,
APIKey: os.Getenv("DAKERA_API_KEY"),
})
// Memory 1: UI preference
imp1 := float32(0.8)
_, err := client.StoreMemory(ctx, "my-agent", dakera.StoreMemoryRequest{
Content: "User prefers dark mode and minimal UI animations",
Importance: &imp1,
Tags: []string{"preference", "ui"},
})
if err != nil { panic(err) }
// Memory 2: Team process
imp2 := float32(0.7)
_, err = client.StoreMemory(ctx, "my-agent", dakera.StoreMemoryRequest{
Content: "Team standup is every morning at 9:30 AM in the #daily channel",
Importance: &imp2,
Tags: []string{"team", "process"},
})
// Memory 3: Infrastructure
imp3 := float32(0.9)
_, err = client.StoreMemory(ctx, "my-agent", dakera.StoreMemoryRequest{
Content: "The deployment pipeline uses GitHub Actions with a staging environment on port 8080",
Importance: &imp3,
Tags: []string{"infra", "deployment"},
})use dakera_client::{DakeraClient, StoreMemoryRequest};
let client = DakeraClient::builder(
std::env::var("DAKERA_API_URL").unwrap_or_else(|_| "http://localhost:3000".into()),
)
.api_key(std::env::var("DAKERA_API_KEY").unwrap_or_default())
.build()?;
// Memory 1: UI preference
client.store_memory(
StoreMemoryRequest::new("my-agent", "User prefers dark mode and minimal UI animations")
.with_importance(0.8)
.with_tags(vec!["preference".into(), "ui".into()]),
).await?;
// Memory 2: Team process
client.store_memory(
StoreMemoryRequest::new("my-agent", "Team standup is every morning at 9:30 AM in the #daily channel")
.with_importance(0.7)
.with_tags(vec!["team".into(), "process".into()]),
).await?;
// Memory 3: Infrastructure
client.store_memory(
StoreMemoryRequest::new("my-agent", "The deployment pipeline uses GitHub Actions with a staging environment on port 8080")
.with_importance(0.9)
.with_tags(vec!["infra".into(), "deployment".into()]),
).await?;Each store call returns the stored memory and the embedding time (the SDKs unwrap memory for you; values here are illustrative):
{
"memory": {
"id": "mem_18a3b7c92d4f1e06",
"memory_type": "episodic",
"content": "User prefers dark mode and minimal UI animations",
"agent_id": "my-agent",
"importance": 0.8,
"tags": ["preference", "ui"],
"created_at": 1778827500,
"last_accessed_at": 1778827500,
"access_count": 0
},
"embedding_time_ms": 42
}
Recall with semantic search
Now ask a question that doesn't match the exact wording of any memory. Dakera's semantic search finds the right one anyway:
curl -X POST http://localhost:3000/v1/memory/recall \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{"agent_id": "my-agent", "query": "how does the team deploy code?"}'response = client.recall(
agent_id="my-agent",
query="how does the team deploy code?"
)
for m in response.memories:
print(f"{m.content} (score: {m.score:.2f})")const response = await client.recall('my-agent', 'how does the team deploy code?');
response.memories.forEach(m =>
console.log(`${m.content} (score: ${m.score.toFixed(2)})`)
);response, err := client.Recall(ctx, "my-agent", dakera.RecallRequest{
Query: "how does the team deploy code?",
})
for _, m := range response.Memories {
fmt.Printf("%s (score: %.2f)\n", m.Content, m.Score)
}use dakera_client::RecallRequest;
let response = client
.recall(RecallRequest::new("my-agent", "how does the team deploy code?"))
.await?;
for m in &response.memories {
println!("{} (score: {:.2})", m.content, m.score);
}Expected response shape — Memory 3 should rank highest even though your query said "deploy code" while the memory says "deployment pipeline":
{
"memories": [
{
"memory": {
"id": "mem_18a3b7ca1e5f2a09",
"content": "The deployment pipeline uses GitHub Actions with a staging environment on port 8080",
"importance": 0.9,
"tags": ["infra", "deployment"],
"created_at": 1778827502
// …agent_id, memory_type, last_accessed_at, access_count
},
"score": 0.92,
"smart_score": 0.81
},
// …the standup memory, with a lower score
],
"query_embedding_time_ms": 18,
"search_time_ms": 6,
"rerank_report": { "applied": true, "candidates": 3 }
}
The scores above are illustrative. rerank_report says whether the cross-encoder ran, over how many candidates and, when it did not, why (skipped); the SDKs expose each result's memory fields and score directly.
Group memories with sessions
Sessions group related memories, for example one conversation, task or debugging run:
# Start a session
curl -X POST http://localhost:3000/v1/sessions/start \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{"agent_id": "my-agent", "metadata": {"task": "onboarding"}}'
# → {"session": {"id": "sess_18a3b8e01a2c3d04", ...}}
# Store a memory within the session
curl -X POST http://localhost:3000/v1/memory/store \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"session_id": "sess_18a3b8e01a2c3d04",
"content": "User completed the quickstart guide and stored 3 test memories",
"importance": 0.6
}'
# End the session with a summary
curl -X POST http://localhost:3000/v1/sessions/sess_18a3b8e01a2c3d04/end \
-H "Authorization: Bearer my-dev-key" \
-H "Content-Type: application/json" \
-d '{"summary": "Completed user onboarding — stored preferences, team info, and infra context"}'# Start a session
session = client.start_session(
agent_id="my-agent",
metadata={"task": "onboarding"}
)
# Store a memory within the session
client.store_memory(
agent_id="my-agent",
session_id=session["id"],
content="User completed the quickstart guide and stored 3 test memories",
importance=0.6
)
# End the session with a summary
client.end_session(
session_id=session["id"],
summary="Completed user onboarding — stored preferences, team info, and infra context"
)// Start a session
const session = await client.startSession('my-agent', { task: 'onboarding' });
// Store a memory within the session
await client.storeMemory('my-agent', {
session_id: session.id,
content: 'User completed the quickstart guide and stored 3 test memories',
importance: 0.6,
});
// End the session with a summary (dakera-js 0.12.2 and later)
await client.endSession(session.id, { summary: 'Completed the quickstart' });// Start a session
session, err := client.StartSession(ctx, dakera.StartSessionRequest{
AgentID: "my-agent",
Metadata: map[string]interface{}{"task": "onboarding"},
})
// Store a memory within the session
sessImp := float32(0.6)
client.StoreMemory(ctx, "my-agent", dakera.StoreMemoryRequest{
SessionID: session.ID,
Content: "User completed the quickstart guide and stored 3 test memories",
Importance: &sessImp,
})
// End the session
client.EndSession(ctx, session.ID) // dakera-go v0.12.2 cannot send a summary// Start a session (start_session_with_metadata attaches metadata)
let session = client.start_session("my-agent").await?;
// Store a memory within the session
client.store_memory(
StoreMemoryRequest::new("my-agent", "User completed the quickstart guide and stored 3 test memories")
.with_importance(0.6)
.with_session(session.id.clone()),
).await?;
// End the session with a summary
client.end_session(
&session.id,
Some("Completed user onboarding — stored preferences, team info, and infra context".to_string()),
).await?;A session carries metadata and, when it ends, an optional summary (or, over REST, "auto_summarize": true to have the server write one). Pass session_id in a recall request to search only that session's memories. The current Go and TypeScript SDKs end a session without a summary.
What's next?