Native App SDK

This guide walks through integrating Boei's AI chatbot into a native mobile or game app with full support for human (live agent) takeover. You build the chat UI in your app, Boei handles the AI intelligence and routing to your live agents.

How to get there: Get your API key from your profile icon (top right) → API Keys.

Full API reference: Boei API or https://app.boei.help/docs/api


1. The mental model

A conversation in Boei has 3 possible modes:

Mode What's happening
ai The AI is responding. Each message you send gets an immediate AI reply.
waiting_for_agent The user asked for a human. They're in queue, no agent assigned yet.
live A human agent has joined and is replying.

Your app's job:

  1. Send user messages with POST /messages.
  2. While in ai mode, the reply comes back inline in the same response.
  3. While in waiting_for_agent or live mode, replies arrive asynchronously, so you poll GET /messages every few seconds to pick them up.
  4. When the user taps "Talk to a human", call POST /escalate.

That's the whole flow.


2. Authentication

Every request needs your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Get your API key from Settings → API in your Boei dashboard.

The chatbot UUID is the one shown on the bot's edit page (it appears in the URL).


3. The 4 endpoints you'll use

3.1 Send a message

POST /api/v1/chatbots/{chatbot_id}/messages

{
  "message": "How do I deposit?",
  "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": { "name": "Vitória", "email": "v@example.com" }
}

  • Omit conversation_id on the very first message. The response gives you back a new one. Store it locally and reuse it for every following message.
  • contact is optional, but recommended so the agent sees who they're talking to.

Response while in AI mode:

{
  "message_id": "msg_001",
  "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
  "response": "You can deposit via Pix or credit card from the Wallet screen.",
  "sources": [...],
  "usage": { "credits_used": 1, ... }
}

Response while in live/waiting mode:

{
  "message_id": "msg_001",
  "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
  "mode": "live",
  "delivered": true
}

Notice there's no response field in live mode. That's how your app knows the message went to a human and the agent's reply will come in via polling.


3.2 Poll for new messages

GET /api/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/messages?after_id=12345

after_id is the id of the last message you've already shown the user. On the first poll, use 0 (or omit it).

Response:

{
  "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
  "mode": "live",
  "waiting_for_agent": false,
  "agent": { "name": "Sarah" },
  "is_closed": false,
  "messages": [
    {
      "id": 12346,
      "role": "agent",
      "sender_name": "Sarah",
      "content": "Hi Vitória, I can help with that.",
      "created_at": "2026-05-11T10:30:05Z"
    }
  ],
  "last_message_id": 12346
}

Use last_message_id as your after_id for the next poll.

Polling cadence:

  • Every 3-5 seconds while the chat screen is open.
  • Stop polling when the chat screen is closed (save battery).
  • If is_closed: true, stop polling and show "Chat ended" in the UI.

Roles you'll see in messages:

  • user – the visitor (your app user)
  • assistant – the Boei AI
  • agent – a human support rep
  • system – occasional system notes (rare, you can ignore or display in muted style)

3.3 Request live chat (escalate)

POST /api/v1/chatbots/{chatbot_id}/conversations/{conversation_id}/escalate

{
  "reason": "User asked to speak with support"
}

reason is optional but helps agents triage.

Possible responses:

{ "status": "queued", "mode": "waiting_for_agent",
  "message": "You're in the queue. An agent will join shortly." }

{ "status": "no_agents_available", "mode": "ai",
  "message": "No agents are currently available. Please try again later." }

{ "status": "already_escalated", "mode": "live", ... }

After a successful escalation:

  • Switch the UI to live-chat mode.
  • Start polling GET /messages if you weren't already.

3.4 Get full conversation (optional)

GET /api/v1/chatbots/{chatbot_id}/conversations/{conversation_id}

Use this when the user re-opens the app and you want to restore the full message history.


4. UI flow & screens

A simple, complete Cocos Creator chat screen needs three states.

State A: AI chatting (default)

┌─────────────────────────────────────────┐
│  Support                          [×]   │
├─────────────────────────────────────────┤
│                                         │
│  [Bot]  Hi! How can I help today?       │
│                                         │
│              How do I deposit? [User]   │
│                                         │
│  [Bot]  You can deposit via Pix or      │
│         credit card from the Wallet.    │
│                                         │
│                                         │
├─────────────────────────────────────────┤
│  [Type a message...........] [Send]     │
│  [ Talk to a human ]                    │
└─────────────────────────────────────────┘

  • Show a bubble per message, AI on the left, user on the right.
  • A "Talk to a human" button somewhere visible (header or above the input).

State B: Waiting for an agent

After tapping "Talk to a human" (which calls POST /escalate):

┌─────────────────────────────────────────┐
│  Support                          [×]   │
├─────────────────────────────────────────┤
│                                         │
│  [System]  You're in the queue. An      │
│            agent will join shortly.     │
│                                         │
│            ⟳ Waiting for an agent...    │
│                                         │
├─────────────────────────────────────────┤
│  [Type a message...........] [Send]     │
└─────────────────────────────────────────┘

  • Show a small spinner or "Waiting for an agent..." banner.
  • Hide the "Talk to a human" button (already done).
  • User can keep typing. Messages are buffered and delivered to the agent when one joins.

State C: Live agent chatting

When the poll response returns agent: { name: "Sarah" }:

┌─────────────────────────────────────────┐
│  Sarah from Support               [×]   │
├─────────────────────────────────────────┤
│                                         │
│  [System]  Sarah has joined the chat.   │
│                                         │
│  [Sarah] Hi Vitória, I can help with    │
│          that.                          │
│                                         │
├─────────────────────────────────────────┤
│  [Type a message...........] [Send]     │
└─────────────────────────────────────────┘

  • Update the header to show the agent's name.
  • Style agent bubbles distinctly from AI bubbles (different color or avatar).

State D: Chat closed

When poll returns is_closed: true:

┌─────────────────────────────────────────┐
│  Support                          [×]   │
├─────────────────────────────────────────┤
│                                         │
│  ...previous messages...                │
│                                         │
│  ─── Chat ended ───                     │
│                                         │
│  [ Start a new chat ]                   │
│                                         │
└─────────────────────────────────────────┘

  • Stop polling.
  • Offer a button to start a new conversation (just clear the stored conversation_id and start fresh).

5. Pseudocode for the chat client

// State
let conversationId = null;
let lastMessageId = 0;
let mode = 'ai';           // 'ai' | 'waiting_for_agent' | 'live'
let pollHandle = null;

const HEADERS = {
  'Authorization': 'Bearer YOUR_API_KEY',
  'Content-Type': 'application/json',
};

// 1. Send user message
async function sendMessage(text) {
  const res = await fetch(`/api/v1/chatbots/${BOT_ID}/messages`, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({
      message: text,
      conversation_id: conversationId,
      contact: { name: userName, email: userEmail },
    }),
  });
  const data = await res.json();
  conversationId = data.conversation_id;

  appendBubble({ role: 'user', content: text });

  if (data.response) {
    // AI mode: reply is inline
    appendBubble({ role: 'assistant', content: data.response });
  } else {
    // Live mode: agent reply will come via polling
    mode = data.mode;
    startPolling();
  }
}

// 2. Escalate to human
async function requestLiveChat() {
  const res = await fetch(
    `/api/v1/chatbots/${BOT_ID}/conversations/${conversationId}/escalate`,
    { method: 'POST', headers: HEADERS, body: JSON.stringify({}) }
  );
  const data = await res.json();
  mode = data.mode;
  showSystemBubble(data.message);
  if (data.status === 'queued') startPolling();
}

// 3. Poll loop
function startPolling() {
  if (pollHandle) return;
  pollHandle = setInterval(pollOnce, 4000);
}
function stopPolling() {
  clearInterval(pollHandle);
  pollHandle = null;
}

async function pollOnce() {
  const url = `/api/v1/chatbots/${BOT_ID}/conversations/${conversationId}/messages?after_id=${lastMessageId}`;
  const res = await fetch(url, { headers: HEADERS });
  const data = await res.json();

  mode = data.mode;
  for (const msg of data.messages) {
    appendBubble(msg);
    lastMessageId = msg.id;
  }
  if (data.agent) setHeader(`${data.agent.name} from Support`);
  if (data.is_closed) {
    stopPolling();
    showChatEnded();
  }
}


6. Tips & gotchas

  • Conversation persistence: store conversation_id and lastMessageId in your app's local storage so the chat survives app restarts.
  • Polling frequency: 3-5s is a good balance. Don't go below 2s. Stop polling when the chat screen isn't visible.
  • Agent identity: the agent field is only present once a human has been assigned. Don't show "Sarah is typing" speculatively, wait for the field.
  • Contact info: passing contact.email is highly recommended for iGaming. It links the chat to your customer record and lets agents send follow-up emails after the chat ends.
  • Closed chats: once is_closed: true, sending a new message will automatically reopen the conversation. But the cleaner UX is to start a fresh conversation_id.
  • Rate limits: standard API throttling applies. If you ever get a 429, back off and retry after the time indicated in the Retry-After header.
  • Error handling: all errors return JSON with error and message fields and a non-2xx status code.

7. Quick test with curl

# Send first message (no conversation_id)
curl -X POST https://app.boei.help/api/v1/chatbots/$BOT_ID/messages \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello"}'

# Escalate (use conversation_id from previous response)
curl -X POST https://app.boei.help/api/v1/chatbots/$BOT_ID/conversations/$CONV_ID/escalate \
  -H "Authorization: Bearer $API_KEY"

# Poll for agent replies
curl https://app.boei.help/api/v1/chatbots/$BOT_ID/conversations/$CONV_ID/messages?after_id=0 \
  -H "Authorization: Bearer $API_KEY"


If you run into trouble, contact support@boei.help and we'll help you debug the integration.