Skip to main content
Start with the simple API and move to the advanced flow when you need more control.

Simple Chat Flow

Send Message Simple API is the quickest way to interact with Onyx programmatically. This endpoint has fewer required parameters and returns a concise JSON payload described by ChatBasicResponse.
You must specify either a chat_session_id or a persona_id in your request.
import requests

API_BASE_URL = "https://cloud.onyx.app/api" # or your own domain
API_KEY = "YOUR_KEY_HERE"

headers = {
  "Authorization": f"Bearer {API_KEY}",
  "Content-Type": "application/json"
}

'''
- To create a chat session, see the Full-featured Chat Flow guide below.
- To find your persona_id, see the List Agents endpoint in the Agents API Reference.
'''

response = requests.post(
  f"{API_BASE_URL}/chat/send-message-simple-api",
  headers=headers,
  json={
    "message": "What is Onyx?",
    "persona_id": 0  # The default Onyx Search Agent (int)
  }
)

data = response.json()
answer = data.get("answer")
answer_citationless = data.get("answer_citationless")
message_id = data.get("message_id")
docs = data.get("top_documents") or []
top_documents = [
  {
    "id": doc.get("document_id"),
    "title": doc.get("semantic_identifier"),
    "link": doc.get("link")
  } for doc in docs
]
cited_docs_map = data.get("cited_documents") or {}

print(f"Answer: {answer}")
print(f"Answer (citationless): {answer_citationless}")
print(f"Message ID: {message_id}")
print(f"Top Documents: {top_documents[:5]}") # Print top 5 documents
print(f"Citations map: {cited_docs_map}")

Advanced Chat Flow

The /send-message endpoint is what the Onyx frontend uses to send and receive messages. Responses are streamed back in packets of JSON objects. To understand the response format, see the Core Concepts documentation.
1

Prepare your request

import requests

API_BASE_URL = "https://cloud.onyx.app/api" # or your own domain
API_KEY = "YOUR_KEY_HERE"

headers = {
  "Authorization": f"Bearer {API_KEY}",
  "Content-Type": "application/json"
}
2

Create a chat session

session_response = requests.post(
  f"{API_BASE_URL}/chat/create-chat-session",
  headers=headers,
  json={
    "persona_id": 0, # The default Onyx Search Agent
    "description": "Onyx API Guide Chat Session"
  }
)

chat_session_id = session_response.json()["chat_session_id"]
3

Send message

payload = {
  "chat_session_id": chat_session_id,
  "parent_message_id": None,
  "message": "What is Onyx?",
  "file_descriptors": [],
  "search_doc_ids": [],
  "retrieval_options": {}
}

# Consume the streamed packets fully and then print a summary
# NOTE: The first packet always contains message IDs:
#   {"user_message_id": int, "reserved_assistant_message_id": int}
with requests.post(
  f"{API_BASE_URL}/chat/send-message",
  headers=headers,
  json=payload,
  stream=True,
) as resp:
  resp.raise_for_status()

  full_text = []
  docs_raw = None
  citations_map = {}
  user_message_id = None
  reserved_assistant_message_id = None

  # Each line is a JSON Packet: { ind: int, obj: { type: "...", ... } }
  for line in resp.iter_lines(decode_unicode=True):
    if not line:
      continue
    try:
      packet = json.loads(line)
    except Exception:
      continue

    # First packet: message IDs
    if "user_message_id" in packet and "reserved_assistant_message_id" in packet:
      user_message_id = packet.get("user_message_id")
      reserved_assistant_message_id = packet.get("reserved_assistant_message_id")
      continue

    obj = packet.get("obj", {})
    ptype = obj.get("type")

    if ptype == "message_start":
      # Includes final search documents per API spec
      docs_raw = obj.get("final_documents") or docs_raw
    elif ptype == "message_delta":
      delta = obj.get("content", "")
      full_text.append(delta)
    elif ptype == "citation_delta":
      # Update citation map using citation_num -> document_id pairs
      if isinstance(obj.get("citations"), list):
        for c in obj["citations"]:
          cnum = c.get("citation_num")
          did = c.get("document_id")
          if cnum is not None and did is not None:
            citations_map[str(cnum)] = str(did)
    elif ptype == "stop":
      break

# Prepare documents (id/title/link) if present
top_documents = []
if isinstance(docs_raw, list):
  for d in docs_raw:
    top_documents.append(
      {
        "id": d.get("document_id") or d.get("id"),
        "title": d.get("semantic_identifier") or d.get("title"),
        "link": d.get("link") or d.get("url"),
      }
    )

print("Final Answer:\n" + "".join(full_text))
if user_message_id is not None:
  print(f"\nUser message ID: {user_message_id}")
if reserved_assistant_message_id is not None:
  print(f"Reserved assistant message ID: {reserved_assistant_message_id}")
print("\nTop Documents:")
print(json.dumps(top_documents, indent=2))
print("\nCitations map (citation_num -> document_id):")
print(json.dumps(citations_map, indent=2))

Next Steps

I