Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.karta.sh/llms.txt

Use this file to discover all available pages before exploring further.

Karta is streaming-first. A turn is not a single batched response; it’s a sequence of typed events. The HTTP API, the CLI, and the SDK all speak the same model, and non-streaming callers just accumulate the stream into a final message. This buys you progressive rendering, long turns without timeouts, granular permission prompts, and one uniform model across every entry point.

The event shape

Every event has the same envelope:
@dataclass
class StreamEvent:
    type: str                      # the event type (below)
    source: str = "karta"
    session_id: str | None = None
    timestamp: int | None = None
    data: dict = {}                # type-specific payload
    raw: dict | None = None        # the harness's original event, untouched
In the SDK you iterate over them:
async for event in app.stream("Explain quicksort"):
    if event.type == "text":
        print(event.text, end="", flush=True)
    elif event.type == "tool_use":
        print(f"\n[tool] {event.data['tool_name']}")
    elif event.type == "done":
        print(f"\n[usage] {event.data['usage']}")

Event types

Each type below is an event.type value. The headings double as anchors (e.g. #input_required).

text

An assistant text chunk. The text is in data.part.text (Claude SDK shape); the SDK’s event.text accessor normalizes that for you.

tool_use

The agent invoked a tool. Payload: tool_name, tool_input, tool_use_id.

reasoning

An extended-thinking / reasoning block, when the model is configured to emit it.

step_start

An agentic step began. Payload includes a step_index and a description.

step_finish

An agentic step completed, with its result.

input_required

The turn is paused awaiting an approval decision (tool use, file write, …). Payload: request_id, kind, tool, message, and options. Resolve it with approve_once, approve_session, or deny. See Inputs.

error

An agent or harness error. Payload: message and an error type/code. In streaming mode, errors arrive as an event rather than an HTTP status — for example a rejected BYOK key surfaces here mid-stream.

done

The turn is complete. Payload carries usage (input_tokens, output_tokens, total_tokens) and the assembled message.
A system type also exists for harness-internal messages; it’s typically hidden by CLI policy. Hide any types you don’t want surfaced with cli.hidden_event_types.

On the wire (SSE)

Over HTTP, events are Server-Sent Events framed as:
event: text
data: {"type":"text","session_id":"...","data":{"part":{"type":"text","text":"Quick"}}}

event: text
data: {"type":"text","session_id":"...","data":{"part":{"type":"text","text":"sort"}}}

event: done
data: {"type":"done","data":{"usage":{"input_tokens":42,"output_tokens":128,"total_tokens":170}}}
curl -N https://api.karta.sh/v1/sessions/$SID/messages \
  -H "Authorization: Bearer $KARTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Explain quicksort","stream":true}'

Accumulating to a final message

Non-streaming callers (send / send_sync, or "stream": false over HTTP) get the same turn assembled into a single Response — text plus the user and assistant messages plus usage. Internally a TurnAccumulator folds the event stream down; the typed model is the primitive, and request/response is just the accumulated form of it.

Messages API

Send and stream over HTTP, with full SSE framing.

Consumer adapters

The same stream, re-shaped into OpenAI and Anthropic wire formats.