BookbagBookbag

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 Markdown

This 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.

warning

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-Remaining header and back off on 429.
  • Retry 429 and 5xx with exponential backoff; fix and do not retry 4xx.

The full error envelope and the code table live in Errors.