UnieAI Studio

Quickstart

Call stateless parsing, knowledge-base search, and OpenAI-compatible Runtime endpoints over HTTP.

The following HTTP endpoints cover parsing, knowledge-base search, chat, and embeddings:

  • Document parsing — POST /api/parse/pdf, POST /api/parse/image
  • KB hybrid search — POST /v1/kb/search
  • OpenAI-compatible chat — POST /v1/chat/completions
  • Embeddings — POST /v1/embeddings

Authentication

Every request needs a UnieAI Studio API key sent as Authorization: Bearer. For one key that works with every example on this page, select your personal workspace and create a personal key under Workspace → API Keys. The current Studio-hosted /api/parse/* routes authenticate personal user keys only; /v1/kb/search and the Runtime endpoints also support their applicable project or organization key flows.

An administrator must also configure a runtime-ready VLM parser under Admin panel → System Models → UnieAI VLM parser, and the key owner must be able to use that model, before PDF or image parsing can succeed.

const STUDIO_BASE_URL = "https://your-studio-domain.com";
const RUNTIME_BASE_URL = "https://your-runtime-domain.com";
const API_KEY = "sk-your-api-key";

async function readJson(response) {
  const payload = await response.json().catch(() => null);
  if (!response.ok) {
    const error = new Error(payload?.error?.message || `HTTP ${response.status}`);
    error.status = response.status;
    error.code = payload?.error?.code || null;
    error.requestId = payload?.error?.request_id || response.headers.get("x-request-id");
    throw error;
  }
  return payload;
}
import requests

STUDIO_BASE_URL = "https://your-studio-domain.com"
RUNTIME_BASE_URL = "https://your-runtime-domain.com"
HEADERS = {"Authorization": "Bearer sk-your-api-key"}
export UNIEAI_STUDIO_URL="https://your-studio-domain.com"
export UNIEAI_RUNTIME_URL="https://your-runtime-domain.com"
export UNIEAI_API_KEY="sk-your-api-key"

Parser endpoints (/api/parse/*) are served by Studio Web, while /v1/* endpoints are served by Runtime. Use separate base URLs unless your deployment exposes both through one gateway. Run these examples on a trusted backend; do not place a long-lived API key in browser code.

Parse a document

The example below parses a PDF into structured pages containing text, VLM transcription, and figure metadata. Only the PDF endpoint supports chunk=true; the returned chunks are not persisted or indexed. The image endpoint returns a single content value and figure metadata and ignores the chunk option.

import { readFile } from "node:fs/promises";

const bytes = await readFile("document.pdf");
const form = new FormData();
form.append("file", new Blob([bytes], { type: "application/pdf" }), "document.pdf");

const response = await fetch(`${STUDIO_BASE_URL}/api/parse/pdf?chunk=true`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}` },
  body: form,
});
const result = await readJson(response);

console.log(result.data.page_count);
console.log(result.data.pages[0].summaryContent);
with open("document.pdf", "rb") as f:
    resp = requests.post(
        f"{STUDIO_BASE_URL}/api/parse/pdf?chunk=true",
        headers=HEADERS,
        files={"file": ("document.pdf", f, "application/pdf")},
    )
resp.raise_for_status()
print(resp.json()["data"]["page_count"])
curl -X POST "$UNIEAI_STUDIO_URL/api/parse/pdf?chunk=true" \
  -H "Authorization: Bearer $UNIEAI_API_KEY" \
  -F "file=@document.pdf"

Search a knowledge base

Run hybrid (dense vector + BM25) search over an existing knowledge base that the API-key identity can access. This endpoint searches already-ingested content; it does not upload or index documents. Inline figure references in each result's content are resolved to loadable signed URLs.

const response = await fetch(`${RUNTIME_BASE_URL}/v1/kb/search`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    kb_id: "kb_xxx",
    query: "How many days of paternity leave?",
    limit: 5,
  }),
});
const { data } = await readJson(response);

for (const item of data.results) {
  console.log(item.score, item.content);
}
resp = requests.post(
    f"{RUNTIME_BASE_URL}/v1/kb/search",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"kb_id": "kb_xxx", "query": "How many days of paternity leave?", "limit": 5},
)
for item in resp.json()["data"]["results"]:
    print(item["score"], item["content"])
curl -X POST "$UNIEAI_RUNTIME_URL/v1/kb/search" \
  -H "Authorization: Bearer $UNIEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kb_id":"kb_xxx","query":"How many days of paternity leave?","limit":5}'

Chat with a RAG-enabled Customize Model

chat calls the OpenAI-compatible Chat Completions endpoint. To use RAG, first attach knowledge bases to the Customize Model and configure its RAG workspace. Runtime uses the knowledge bases assigned to that model; a request cannot select arbitrary knowledge bases with knowledgeBaseIds.

const response = await fetch(`${RUNTIME_BASE_URL}/v1/chat/completions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "your-model-slug",
    messages: [{ role: "user", content: "How many days of paternity leave?" }],
    stream: false,
  }),
});
const completion = await readJson(response);
console.log(completion.choices[0].message.content);

Streaming

const res = await fetch(`${RUNTIME_BASE_URL}/v1/chat/completions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
    Accept: "text/event-stream",
  },
  body: JSON.stringify({
    model: "your-model-slug",
    messages: [{ role: "user", content: "Summarize this policy in three bullets" }],
    stream: true,
  }),
});

if (!res.ok) throw new Error(`Chat failed with HTTP ${res.status}`);

const reader = res.body.getReader();
const decoder = new TextDecoder();
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}

Error handling

Error responses use the platform error shape. The readJson helper above converts them into an Error with status, code, and requestId fields.

try {
  const response = await fetch(`${RUNTIME_BASE_URL}/v1/kb/search`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ kb_id: "kb_xxx", query: "..." }),
  });
  await readJson(response);
} catch (err) {
  console.error(err.status, err.code, err.requestId, err.message);
}
codestatusdescription
unauthorized401Missing or invalid API key
invalid_request400Missing required field or bad parameter
forbidden403No access to that knowledge base