Newer
Older
navi-1 / docs / api.md
@Eugene Sukhodolskiy Eugene Sukhodolskiy on 22 Apr 13 KB Use gemma4 cloud model by default

Navi API Reference

Base URL: http://localhost:8000


REST API

Health

GET /health

Server availability check.

Response 200

{ "status": "ok" }

Profiles & Tools

GET /agents/profiles

List available agent profiles.

Response 200

[
  {
    "id": "secretary",
    "name": "Personal Secretary",
    "description": "General-purpose assistant",
    "enabled_tools": ["todo", "web_search", "filesystem", "..."],
    "llm_backend": "ollama",
    "model": "gemma4:31b-cloud"
  }
]

GET /agents/tools

List all registered tools (built-in + user tools).

Response 200

[
  { "name": "web_search", "description": "Search the web using DuckDuckGo." },
  { "name": "filesystem",  "description": "Read, write and list files." }
]

Sessions

POST /sessions

Create a new session.

Request body

{ "profile_id": "secretary" }

Response 201

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "profile_id": "secretary",
  "created_at": "2026-04-10T18:00:00+00:00"
}

Errors

  • 404 — profile not found

GET /sessions

List all sessions sorted by activity (pinned first).

Response 200

[
  {
    "session_id": "550e8400-...",
    "profile_id": "secretary",
    "name": "Research task",
    "message_count": 12,
    "preview": "Last 60 chars of the most recent message",
    "pinned": false,
    "created_at": "2026-04-10T15:00:00+00:00",
    "last_active": "2026-04-10T18:00:00+00:00"
  }
]

name is null until POST /sessions/{id}/generate-name is called.


GET /sessions/{session_id}

Full session with message history (display history — never compressed).

Response 200

{
  "session_id": "550e8400-...",
  "profile_id": "secretary",
  "name": "Research task",
  "context_token_count": 4913,
  "max_context_tokens": 65536,
  "created_at": "...",
  "last_active": "...",
  "messages": [
    {
      "role": "user",
      "content": "Hello",
      "created_at": "2026-04-10T18:00:00+00:00"
    },
    {
      "role": "assistant",
      "content": "Hi. How can I help?",
      "created_at": "2026-04-10T18:00:05+00:00"
    },
    {
      "role": "assistant",
      "tool_calls": [
        {
          "id": "abc123",
          "name": "web_search",
          "arguments": { "query": "..." }
        }
      ]
    },
    {
      "role": "tool",
      "content": "tool result",
      "tool_call_id": "abc123",
      "name": "web_search"
    }
  ]
}

Message fields (role is always present, others by availability):

Field Type Description
role `user\ assistant\ tool\ system` Message author
content `string\ null` Text content
images string[] Base64 images (user/assistant)
tool_calls ToolCall[] Tool invocations (assistant)
tool_call_id string ID of the call this result belongs to (tool)
name string Tool name (tool messages)
thinking `string\ null` LLM reasoning captured during a tool-calling turn
is_plan bool Planning phase output — rendered as a plan card, not text
is_compression bool Marker injected when context compression ran
is_summary bool Summary message replacing compressed history
created_at string (ISO 8601) Creation time
elapsed_seconds `number\ null` Time to complete the turn (final assistant message)
tool_call_count `number\ null` Number of tool calls in the turn
token_count `number\ null` Tokens used in the turn

Errors

  • 404 — session not found

DELETE /sessions/{session_id}

Delete a session and its files.

Response 204 — no body

Errors

  • 404 — session not found

PATCH /sessions/{session_id}/pin

Pin or unpin a session.

Request body

{ "pinned": true }

Response 200

{ "session_id": "...", "pinned": true }

POST /sessions/{session_id}/generate-name

Generate a short display name for a session from its message history. Called automatically by the client after the first exchange. No-op if the session already has a name.

Response 200

{ "name": "Web search for recipes" }

Returns {"name": null} if there are no user messages yet.

Errors

  • 404 — session not found

GET /sessions/{session_id}/context

LLM context (what the model actually sees). May differ from messages — compressed history replaces old turns with a summary. Debug endpoint.

Response 200

{
  "session_id": "...",
  "profile_id": "secretary",
  "message_count": 8,
  "total_chars": 4200,
  "context": [ ...same format as messages... ]
}

GET /sessions/{session_id}/planning

All planning phase debug logs for the session. Each entry is one planning run.

Response 200

{ "session_id": "...", "logs": [ { "phase": "...", "output": "..." }, ... ] }

POST /sessions/{session_id}/files

Upload a file for a session. Call before sending a message to attach the file.

Request: multipart/form-data, field file.

Limits

  • Max size: 200 MB
  • Forbidden extensions: .exe, .dll, .so, .sh, .bat, .cmd, .ps1, .vbs, .bin, .elf, and other executable formats
  • Duplicate filenames get a numeric suffix

Response 201

{
  "name": "report.pdf",
  "size": 102400,
  "path": "session_files/550e8400-.../report.pdf",
  "content_type": "application/pdf"
}

Errors

  • 400 — forbidden extension
  • 404 — session not found
  • 413 — file exceeds limit

GET /sessions/{session_id}/files/{filename}

Download or view an uploaded file. Images, PDFs and plain text are served inline; everything else as an attachment.

Response 200 — file bytes

Errors

  • 403 — path traversal attempt
  • 404 — session or file not found

Messages (non-streaming)

POST /sessions/{session_id}/messages

Send a message and receive a response synchronously (no streaming). Blocks until the full agent loop completes.

Request body

{ "content": "How many stars are in the galaxy?" }

Response 200

{ "role": "assistant", "content": "Estimates range from 100 to 400 billion." }

Errors

  • 404 — session not found
  • 500 — agent error or iteration limit exceeded

For production clients prefer WebSocket — it provides streaming, tool progress, and model reasoning.


WebSocket

WS /ws/sessions/{session_id}

Main channel for real-time agent interaction. Supports text streaming, thinking streaming, tool events, file and image attachment.

Connect: if the session is not found, the server closes with code 4004.

On connect: the server immediately sends session_sync (no active run) or starts the reconnect replay flow (run in progress).


Client → Server

All client messages are JSON objects.

Send a message

{
  "type": "message",
  "content": "Message text",
  "images": ["base64string...", "..."],
  "files": [
    { "name": "report.pdf", "size": 102400, "path": "session_files/.../report.pdf" }
  ]
}
Field Required Description
type yes Always "message"
content yes Message text (non-empty)
images no Base64 image list. Both raw base64 and data:image/...;base64,... are accepted — server strips the prefix
files no Files uploaded via POST /sessions/{id}/files. Server appends their paths to the message content

Server → Client

Events arrive in the order they are emitted.

stream_start

{ "type": "stream_start" }

Processing started. Client should block input.


thinking_delta

{ "type": "thinking_delta", "delta": "reasoning fragment..." }

Streaming chunk of model reasoning. Accumulate until thinking_end.


thinking_end

{ "type": "thinking_end" }

Reasoning phase complete. Next will be stream_delta or tool calls.


turn_thinking

{
  "type": "turn_thinking",
  "thinking": "full reasoning text...",
  "is_subagent": false
}

Complete reasoning block from a tool-calling turn. Not streamed — arrives whole. is_subagent: true means this reasoning came from a subagent inside spawn_agent.


planning_status

{
  "type": "planning_status",
  "phase": "analysis",
  "label": "Analysing request...",
  "is_subagent": false
}

Progress update during the planning phase. phase is one of analysis, reflect, plan. is_subagent: true — route into the spawn_agent card, not the top-level UI.


plan_ready

{
  "type": "plan_ready",
  "plan": "1. Step one\n2. Step two\n...",
  "is_subagent": false
}

Planning complete — full step list. Rendered as a collapsible plan card. is_subagent: true — route into the spawn_agent card.


tool_started

{
  "type": "tool_started",
  "tool": "web_search",
  "args": { "query": "weather in moscow" },
  "is_subagent": false
}

Agent started executing a tool. Arrives before execution completes — show a spinner. is_subagent: true — call from a subagent.


tool_call

{
  "type": "tool_call",
  "tool": "web_search",
  "args": { "query": "weather in moscow" },
  "result": "Today +12°C, cloudy.",
  "success": true,
  "is_subagent": false
}

Tool finished. Arrives after tool_started with the same tool and args. success: false — tool returned an error.


stream_delta

{ "type": "stream_delta", "delta": "response fragment..." }

Streaming chunk of the final text response. Accumulate into a string.


stream_end

{
  "type": "stream_end",
  "content": "full response text",
  "context_tokens": 4913,
  "max_context_tokens": 65536,
  "elapsed_seconds": 12.4,
  "tool_call_count": 3,
  "token_count": 1842
}

Agent finished. content is the full accumulated text (duplicates the sum of stream_delta). Client should unblock input.


stream_stopped

{ "type": "stream_stopped" }

Generation was stopped by POST /sessions/{id}/stop.


profile_switched

{
  "type": "profile_switched",
  "profile_id": "server_admin",
  "profile_name": "Server Administrator"
}

Agent switched profile via switch_profile tool. New profile takes effect on the next user message. Client should update the profile indicator. Arrives during the stream — before tool_call for switch_profile.


context_compressed

{
  "type": "context_compressed",
  "messages_before": 42,
  "messages_after": 12,
  "summary": "User asked about..."
}

Context was automatically compressed (triggers at ≥80% of context window). Informational.


heartbeat

{ "type": "heartbeat" }

Keepalive ping sent every 20 s during long silent operations. Client can ignore.


session_sync

{ "type": "session_sync" }

Client must reload session history from GET /sessions/{id}. Sent:

  1. On connect when no run is active (agent may have finished while disconnected).
  2. After a reconnect-replay flow completes (ensures client sees the fully saved response).

replay_start

{ "type": "replay_start", "count": 14 }

About to replay count buffered events from a mid-stream reconnect. Client should suppress cursor animations and in-progress effects during replay.


replay_end

{ "type": "replay_end" }

Replay complete. Live events will follow.


error

{ "type": "error", "message": "Session not found" }

Processing error. Stream may or may not continue after this.


Typical event sequences

Simple question, no tools:

stream_start
thinking_delta × N   (if model has thinking enabled)
thinking_end
stream_delta × N
stream_end

Request with tool calls:

stream_start
turn_thinking         (reasoning before tool selection, if any)
tool_started
tool_call
turn_thinking         (before next tool, if any)
tool_started
tool_call
thinking_delta × N   (final response reasoning)
thinking_end
stream_delta × N
stream_end
context_compressed    (optional, if context was near full)

Request with planning enabled:

stream_start
planning_status       (phase: analysis)
planning_status       (phase: plan)
plan_ready
turn_thinking
tool_started
tool_call
...
stream_end

Request with subagent (spawn_agent):

stream_start
tool_started          (spawn_agent, is_subagent=false)
  turn_thinking       (is_subagent=true)
  planning_status     (is_subagent=true, if subagent has planning)
  plan_ready          (is_subagent=true, if subagent has planning)
  tool_started        (subagent tool, is_subagent=true)
  tool_call           (is_subagent=true)
tool_call             (spawn_agent done, is_subagent=false)
stream_delta × N
stream_end

Reconnect mid-stream:

stream_start
replay_start          {"count": N}
ev_0 ... ev_N-1       (buffered events replayed verbatim)
replay_end
(live events continue)
...
stream_end
session_sync

Profile switch (switch_profile):

stream_start
tool_started          (switch_profile)
profile_switched      (client updates UI here — before tool_call)
tool_call             (switch_profile done)
stream_delta × N
stream_end

Files

Client static: GET /static/** — served from client/ directory. Header Cache-Control: no-store.

Session uploaded files: stored in session_files/{session_id}/. Agent accesses them via the filesystem tool. Deleted after 24 h of session inactivity or when the session is deleted.


Error codes

HTTP Reason
400 Forbidden file type
404 Session or profile not found
413 File exceeds 200 MB
500 Internal agent error
WS 4004 Session not found on connect