Getting started

Quickstart

Open a conversation and get a streamed reply, in five minutes and three requests.

By the end of this page you will have had a real conversation with your assistant from the command line.

Before you start#

You need two things from Studio.

A published assistant. Open Assistants, pick one, and check it says Published. A draft answers on the dashboard test drive but not over the API — see why.

An API key. Open API → Keys, click Create key, name it something you will recognise in a log six months from now, and copy the key. It is shown once.

Terminal
export ZEEVAA_API_KEY="zv_live_…"
export ZEEVAA_ASSISTANT_ID="9f2b1c84-6e3a-4d17-b0c5-2e7a8f41d9b3"

The assistant id is on the assistant's own page in Studio, and every id in this documentation is a placeholder — yours will differ.

1. Check the key works#

Terminal
curl https://studio.zeevaa.ai/api/v1/assistants \
  -H "Authorization: Bearer $ZEEVAA_API_KEY"
JSON
{
  "assistants": [
    {
      "id": "9f2b1c84-6e3a-4d17-b0c5-2e7a8f41d9b3",
      "name": "Marina sales desk",
      "status": "published",
      "channels": { "chat": true, "voice": true },
      "has_catalogue": true
    }
  ]
}

If this returns 401, the key is wrong or revoked. If it returns an empty list, the key is valid but its workspace has no assistants.

2. Open a conversation#

A session is the conversation. Open one before you say anything.

Terminal
curl -X POST \
  "https://studio.zeevaa.ai/api/v1/assistants/$ZEEVAA_ASSISTANT_ID/sessions" \
  -H "Authorization: Bearer $ZEEVAA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel": "chat"}'
JSON
{
  "session_id": "c31f9a70-84b2-4e05-9d6c-1a7f3b2e6d48",
  "assistant_id": "9f2b1c84-6e3a-4d17-b0c5-2e7a8f41d9b3",
  "greeting": "Hi! How can I help you today?",
  "started_at": "2026-08-04T09:14:22.108Z"
}

Show greeting as the assistant's first message. It is not a turn and does not appear in the transcript — it is the opening line you configured in Studio.

Terminal
export ZEEVAA_SESSION_ID="c31f9a70-84b2-4e05-9d6c-1a7f3b2e6d48"

3. Say something#

Terminal
curl -N -X POST \
  "https://studio.zeevaa.ai/api/v1/assistants/$ZEEVAA_ASSISTANT_ID/chat" \
  -H "Authorization: Bearer $ZEEVAA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Zeevaa-Format: sse" \
  -d "{\"session_id\": \"$ZEEVAA_SESSION_ID\", \"message\": \"What do you have with two bedrooms under 2M?\"}"

-N disables curl's buffering, so you see the reply arrive rather than all at once when it finishes.

HTTP
event: message.delta
data: {"text":"We have three"}

event: tool.call
data: {"name":"search_records","input":{"bedrooms":2,"price":{"lte":2000000}}}

event: canvas
data: {"kind":"results","total":3,"records":[{"title":"Marina Villa","data":{"price":1850000}}]}

event: message.delta
data: {" that match — the Marina Villa at 1.85M is the closest."}

event: done
data: {"usage":{"input_tokens":412,"output_tokens":88},"session_id":"c31f9a70-…"}

Three things happened, and they are worth separating.

The assistant used a tool. tool.call tells you it decided to search the catalogue, and with what filters. You do not have to show this, but it is what makes a "thinking…" state honest rather than decorative.

It found records. The canvas event carries structured data — real fields you can render as cards, not prose describing them. This is the reason to use the API rather than embed a chat window.

It answered. The message.delta events are the text, in order.

4. Don't want a stream?#

Send "stream": false and get one JSON body when it is finished.

Terminal
curl -X POST \
  "https://studio.zeevaa.ai/api/v1/assistants/$ZEEVAA_ASSISTANT_ID/chat" \
  -H "Authorization: Bearer $ZEEVAA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"session_id\": \"$ZEEVAA_SESSION_ID\", \"message\": \"And the cheapest?\", \"stream\": false}"
JSON
{
  "message": {
    "role": "assistant",
    "content": "The Marina Villa at 1.85M is the lowest of the three."
  },
  "tool_calls": [{ "name": "search_records", "status": "success" }],
  "usage": { "input_tokens": 508, "output_tokens": 34 }
}

Notice the second message understood "the cheapest" without being told what of. You did not resend the first exchange. The session holds the transcript; your code sends one message at a time.

5. Close it#

Terminal
curl -X POST \
  "https://studio.zeevaa.ai/api/v1/sessions/$ZEEVAA_SESSION_ID/end" \
  -H "Authorization: Bearer $ZEEVAA_API_KEY"

Ending a session stamps its duration and releases it. If your code never gets the chance — a user closes the tab, a process dies — it will be closed automatically once it goes quiet. Ending it deliberately just makes your transcripts and durations accurate.

What you just built#

Code
POST /sessions          once per conversation
POST /chat              once per message
POST /sessions/…/end    once, at the end

Everything else in this documentation is detail on those three, plus voice, transcripts and usage.

Next#

  • Streaming — the event formats, and which to choose
  • Sessions — lifecycle, visitor details, and what a session actually holds
  • Next.js integration — the same thing from a real app