Skip to content

Conversations

NimbleBrain automatically persists every conversation. Each conversation is stored as a JSONL file, and you can resume, fork, rename, delete, and search conversations through the CLI, API, or web UI.

Conversations are stored as append-only JSONL (JSON Lines) files in ~/.nimblebrain/conversations/. Each file is named by its conversation ID:

~/.nimblebrain/conversations/conv_a1b2c3d4e5f67890.jsonl

The file format:

  • Line 1 — conversation metadata
  • Lines 2+ — one StoredMessage per line
{
"id": "conv_a1b2c3d4e5f67890",
"createdAt": "2026-03-25T10:30:00.000Z",
"updatedAt": "2026-03-25T10:35:42.000Z",
"title": "Meeting notes summary",
"totalInputTokens": 24500,
"totalOutputTokens": 1800,
"totalCostUsd": 0.042,
"lastModel": "claude-sonnet-4-5-20250929"
}

The metadata line is updated in place on each append — the file is rewritten atomically (write to temp file, then rename) to prevent corruption.

Each message contains the role, content, timestamp, and optional metadata:

{
"role": "user",
"content": "Summarize my meetings from this week",
"timestamp": "2026-03-25T10:30:00.000Z"
}

Assistant messages include additional metadata:

{
"role": "assistant",
"content": "Here are your meetings from this week...",
"timestamp": "2026-03-25T10:30:15.000Z",
"metadata": {
"skill": "meeting-notes",
"toolCalls": [
{
"id": "tc_001",
"name": "granola__search_meetings",
"input": {"query": "this week"},
"output": "[{\"title\": \"Sprint Planning\", ...}]",
"ok": true,
"ms": 340
}
],
"inputTokens": 12000,
"outputTokens": 800,
"cacheCreationTokens": 0,
"cacheReadTokens": 8000,
"costUsd": 0.021,
"model": "claude-sonnet-4-5-20250929",
"llmMs": 2400,
"iterations": 2
}
}
Terminal window
nb --resume conv_a1b2c3d4e5f67890

This loads the conversation history and continues from where you left off.

Terminal window
curl http://localhost:27247/v1/conversations
{
"conversations": [
{
"id": "conv_a1b2c3d4e5f67890",
"createdAt": "2026-03-25T10:30:00.000Z",
"updatedAt": "2026-03-25T10:35:42.000Z",
"title": "Meeting notes summary",
"messageCount": 6,
"preview": "Summarize my meetings from this week",
"totalInputTokens": 24500,
"totalOutputTokens": 1800,
"totalCostUsd": 0.042
}
],
"nextCursor": null,
"totalCount": 1
}

Pagination is cursor-based. Pass cursor from nextCursor to get the next page:

Terminal window
curl "http://localhost:27247/v1/conversations?limit=20&cursor=abc123"

Sort by creation or last update:

Terminal window
curl "http://localhost:27247/v1/conversations?sortBy=updatedAt"

Retrieve all messages for a conversation:

Terminal window
curl http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890/history
{
"conversationId": "conv_a1b2c3d4e5f67890",
"messages": [
{"role": "user", "content": "Summarize my meetings", "timestamp": "2026-03-25T10:30:00.000Z"},
{"role": "assistant", "content": "Here are your meetings...", "timestamp": "2026-03-25T10:30:15.000Z"}
]
}

Fork creates a new conversation from an existing one, optionally truncated at a specific message index:

Terminal window
curl -X POST http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890/fork

Fork at a specific point (copies only the first 4 messages):

Terminal window
curl -X POST "http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890/fork?atMessage=4"

The forked conversation gets a new ID and re-accumulates token counts from the copied messages. The original conversation is unchanged.

Update the title:

Terminal window
curl -X PATCH http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890 \
-H "Content-Type: application/json" \
-d '{"title": "Q1 sprint meetings"}'
Terminal window
curl -X DELETE http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890

This deletes the JSONL file from disk. The operation is irreversible.

Search across conversation titles and content:

Terminal window
curl "http://localhost:27247/v1/conversations?search=meeting+notes"

Search is performed at the API level against the conversation index.

NimbleBrain generates conversation titles automatically after the first assistant response. The title is stored in the metadata line of the JSONL file and displayed in the web UI sidebar and conversation list.

By default, the CLI uses JSONL storage in ~/.nimblebrain/conversations/. You can change the storage backend in nimblebrain.json:

{
"store": {
"type": "jsonl",
"dir": "/custom/path/to/conversations"
}
}

For in-memory storage (no persistence):

{
"store": {
"type": "memory"
}
}