REST API integration
A practical walkthrough of building on the Bookbag REST API: authenticate, send a chat message, stream the reply, continue conversations, and read history.
View as MarkdownThis guide walks through a typical backend integration end to end. For the exhaustive parameter-by-parameter contracts, jump to the API reference; here we focus on the happy path and the decisions you will make.
1. Get an API key
Create a key in the dashboard (admin role) and store the bk_... value in your secret manager. Scope the key to a single agent if the integration only touches one. Full details in Authentication.
Call the API from your server, never from the browser. A key grants workspace-level access.
2. Pick a version
- [API v2](/docs/api/v2/overview) — recommended for new integrations. Modern REST, AI-SDK streaming, cursor pagination, rate-limit headers, and machine error codes.
- [API v1](/docs/api/v1/chat) — use it when you need drop-in Chatbase compatibility (especially contacts).
3. Send a message
A v2 chat request is a single POST. Set stream: false while you are getting started so you can work with a plain JSON response:
curl https://app.bookbag.ai/api/v2/agents/123/chat \
-H "Authorization: Bearer $BOOKBAG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "Do you ship to Canada?", "stream": false }'async function ask(agentId, message, conversationId) {
const res = await fetch(
`https://app.bookbag.ai/api/v2/agents/${agentId}/chat`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BOOKBAG_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message, conversationId, stream: false }),
}
);
if (!res.ok) throw new Error((await res.json()).error.code);
const { data } = await res.json();
return {
text: data.parts.map((p) => p.text).join(""),
conversationId: data.metadata.conversationId,
};
}{
"data": {
"id": "msg_789",
"role": "assistant",
"parts": [{ "type": "text", "text": "Yes, we ship to Canada." }],
"metadata": { "conversationId": "456", "finishReason": "stop", "usage": { "credits": 1 } }
}
}4. Continue the conversation
The first reply returns a conversationId in its metadata. Pass it back on the next message to keep context. Optionally pass a userId to group all of one person's conversations so you can later list them per user.
const first = await ask(123, "Do you ship to Canada?"); const second = await ask(123, "How long does it take?", first.conversationId); console.log(second.text);
5. Stream for responsiveness
In a user-facing UI you usually want tokens as they arrive. Drop stream: false (streaming is the default) and parse the v2 streaming format — bare data: lines of text-delta frames ending in data: [DONE].
6. Read history & set feedback
List conversations and messages with cursor pagination, export everything for analytics, and record thumbs up/down. See List conversations and the feedback action on Chat.
Handle errors and limits
- Branch on the
error.code, not the message. The codes are stable; messages are not. - Respect rate limits: 100 requests / 10s per key. Watch the
X-RateLimit-Remainingheader and back off on429. - Retry
429and5xxwith exponential backoff; fix and do not retry4xx.
The full error envelope and the code table live in Errors.