curl --request POST \
--url https://api.corebasehq.com/api/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"content": "<string>"
}
],
"model": "<string>",
"stream": true,
"source_ids": [
"<string>"
],
"project_id": "<string>",
"end_user": "<string>",
"conversation_id": "<string>"
}
'import requests
url = "https://api.corebasehq.com/api/v1/chat/completions"
payload = {
"messages": [{ "content": "<string>" }],
"model": "<string>",
"stream": True,
"source_ids": ["<string>"],
"project_id": "<string>",
"end_user": "<string>",
"conversation_id": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{content: '<string>'}],
model: '<string>',
stream: true,
source_ids: ['<string>'],
project_id: '<string>',
end_user: '<string>',
conversation_id: '<string>'
})
};
fetch('https://api.corebasehq.com/api/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.corebasehq.com/api/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'content' => '<string>'
]
],
'model' => '<string>',
'stream' => true,
'source_ids' => [
'<string>'
],
'project_id' => '<string>',
'end_user' => '<string>',
'conversation_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.corebasehq.com/api/v1/chat/completions"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.corebasehq.com/api/v1/chat/completions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.corebasehq.com/api/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body<string>{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "<string>"
}Create a chat completion
Runs an OpenAI-compatible chat completion over your connected data.
Send a messages array; the agent may query your sources server-side. With
stream: true (default) the answer streams back as Server-Sent Events
(standard chat.completion.chunk objects, terminated by data: [DONE]);
with stream: false the full answer is returned as a single
chat.completion object — use that from tools that can’t consume SSE
(n8n, Zapier, plain HTTP calls). The model field is accepted for OpenAI
compatibility but ignored — the model used is your organization’s default
LLM key. Scope a request to specific sources with source_ids. Requires
the mcp_query permission.
curl --request POST \
--url https://api.corebasehq.com/api/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"content": "<string>"
}
],
"model": "<string>",
"stream": true,
"source_ids": [
"<string>"
],
"project_id": "<string>",
"end_user": "<string>",
"conversation_id": "<string>"
}
'import requests
url = "https://api.corebasehq.com/api/v1/chat/completions"
payload = {
"messages": [{ "content": "<string>" }],
"model": "<string>",
"stream": True,
"source_ids": ["<string>"],
"project_id": "<string>",
"end_user": "<string>",
"conversation_id": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{content: '<string>'}],
model: '<string>',
stream: true,
source_ids: ['<string>'],
project_id: '<string>',
end_user: '<string>',
conversation_id: '<string>'
})
};
fetch('https://api.corebasehq.com/api/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.corebasehq.com/api/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'content' => '<string>'
]
],
'model' => '<string>',
'stream' => true,
'source_ids' => [
'<string>'
],
'project_id' => '<string>',
'end_user' => '<string>',
'conversation_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.corebasehq.com/api/v1/chat/completions"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.corebasehq.com/api/v1/chat/completions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.corebasehq.com/api/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"model\": \"<string>\",\n \"stream\": true,\n \"source_ids\": [\n \"<string>\"\n ],\n \"project_id\": \"<string>\",\n \"end_user\": \"<string>\",\n \"conversation_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body<string>{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "<string>"
}https://api.corebasehq.com/api/v1 and it just works. By default responses stream as text/event-stream using the standard chat.completion.chunk payload, terminated by data: [DONE]; set stream: false to get one complete chat.completion JSON object instead (for workflow tools like n8n or Zapier, and plain HTTP calls).
POST /api/v1/chat/completions is the canonical path. The older POST /api/v1/chat is kept as an alias for backward compatibility and behaves identically.conversation_id from a previous response and send only the new message —
see Multi-turn.Acting on behalf of your users
If your product has users, sendend_user with each completion — a stable id of
whoever the request is for:
{
"messages": [{ "role": "user", "content": "where is my order?" }],
"end_user": "customer_8121",
"stream": false
}
end_user is for a source that isn’t
about a particular person.
Streaming response
Content-Type: text/event-stream. Each frame is a standard OpenAI chat.completion.chunk:
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1716284400,"model":"anthropic/claude-3-5-sonnet-20241022","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1716284400,"model":"anthropic/claude-3-5-sonnet-20241022","choices":[{"index":0,"delta":{"content":"Top "},"finish_reason":null}]}
...
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1716284400,"model":"anthropic/claude-3-5-sonnet-20241022","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
X-Conversation-Id header — keep it to continue the conversation later without resending history.
Non-streaming (stream: false)
One complete chat.completion object, Content-Type: application/json:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1716284400,
"model": "corebase",
"conversation_id": "abc123",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Your top customers are…" },
"finish_reason": "stop"
}
]
}
conversation_id is a CoreBase extension; OpenAI clients ignore it.
Using the OpenAI SDK
Because the wire format matches OpenAI, you can use any OpenAI client:from openai import OpenAI
client = OpenAI(
api_key="cb_live_xxx",
base_url="https://api.corebasehq.com/api/v1",
)
stream = client.chat.completions.create(
model="corebase",
messages=[{"role": "user", "content": "Top 3 customers by revenue this quarter?"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CB_LIVE!,
baseURL: "https://api.corebasehq.com/api/v1",
});
const stream = await client.chat.completions.create({
model: "corebase",
messages: [{ role: "user", content: "Top 3 customers by revenue this quarter?" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0].delta.content ?? "");
}
Tool calls
When the model invokes one of your connected data sources, the call streams as a standard OpenAItool_calls delta:
{
"choices": [{
"index": 0,
"delta": {
"tool_calls": [{
"index": 0,
"id": "call_abc",
"type": "function",
"function": {
"name": "erp_prod__execute_query",
"arguments": "{\"sql\":\"SELECT TOP 3 ...\"}"
}
}]
},
"finish_reason": null
}]
}
Errors
| Status | Meaning |
|---|---|
400 | Body schema invalid |
401 | Missing or invalid token — see Authentication |
403 | Token lacks mcp_query permission |
404 | Unknown project_id, or a conversation_id that doesn’t belong to this token |
409 | No LLM provider key configured for the organization — set one in Settings → LLM keys |
429 | Rate limit hit. Honor Retry-After. |
503 | Transient backend issue — retry with exponential backoff |
Multi-turn
Two ways to continue a conversation — pick one per conversation, don’t mix them.Resend the history (OpenAI-style)
Append assistant replies to themessages array on subsequent calls. The model uses the full conversation as context.
{
"model": "corebase",
"messages": [
{ "role": "user", "content": "Top customers this quarter?" },
{ "role": "assistant", "content": "Acme, Globex, Initech." },
{ "role": "user", "content": "And how do they compare to last quarter?" }
],
"stream": true
}
Continue by conversation_id
Every response returns a conversation_id (as a body field on non-streaming responses, and as the X-Conversation-Id header on both). Pass it back with only the new message — the server keeps the history, so requests stay small and prompt size doesn’t grow with every turn:
// Turn 1 — a new conversation
{ "messages": [{ "role": "user", "content": "Top customers this quarter?" }], "stream": false }
// → { ..., "conversation_id": "abc123" }
// Turn 2 — just the new message
{
"conversation_id": "abc123",
"messages": [{ "role": "user", "content": "And compared to last quarter?" }],
"stream": false
}
404. A continued conversation always stays in the project it started in; project_id is ignored on continuation.Authorizations
Body
An OpenAI-compatible chat completion request. model is accepted but
ignored — the model used is your organization's default LLM key.
Run this completion on behalf of one of your users. Sources scoped to an end user (identity headers, pinned parameters, row-level database scope) resolve to this value, and usage, memory and the audit trail are attributed to it. Omit for a request that isn't about a particular person — scoped sources are then unavailable rather than unscoped.
200Continue a server-stored conversation instead of resending the history. Take the value from a previous response (the conversation_id field / X-Conversation-Id header) and send ONLY the new message(s) in messages — the server prepends the stored history. Omit for a new conversation (fully stateless, backward compatible). Ids are only valid for the token identity that created them.
Response
A Server-Sent Event stream of OpenAI chat.completion.chunk objects, terminated by data: [DONE].
The response is of type string.