API Reference

A simple REST API to read your CRM and conversations, inject messages from any source, pull metrics, and draft AI replies from your knowledge base. JSON in, JSON out.

OpenAPI 3.1 spec — import into Postman / Insomnia →

Authentication

Every request is authenticated with your workspace API key as a Bearer token. Find it in Dashboard → Contacts → Connect your app. Keep it secret — it grants full access to your workspace data.

Authorization: Bearer od_sk_your_workspace_key
Content-Type: application/json

Base URL:  https://onebubbles.com/api/v1

JavaScript SDK

A tiny wrapper for the browser and Node 18+. Or just call the REST endpoints below directly.

<script src="https://onebubbles.com/onebubbles.js"></script>
<script>
  const ob = Onebubbles.createClient("od_sk_...");

  await ob.contacts.upsert({ email: "jane@acme.com", name: "Jane" });
  await ob.messages.send({ externalId: "u-1", message: "Hi there" });
  const { conversations } = await ob.conversations.list({ email: "jane@acme.com" });
  const { reply } = await ob.ai.reply("What are your hours?");
  const stats = await ob.stats();
</script>

// Node 18+ / bundlers:
const Onebubbles = require("onebubbles.com/onebubbles.js"); // or a copy
const ob = Onebubbles.createClient(process.env.ONEBUBBLES_KEY);

Endpoints

POST/api/v1/contacts

Create or update a contact

Upsert a CRM contact by email. Returns the contact id and whether it was newly created.

Parameters
email
required — the contact's email (unique key)
name
optional — display name
phone
optional
tags
optional — array of strings
Request
curl -X POST https://onebubbles.com/api/v1/contacts \
  -H "Authorization: Bearer od_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@acme.com",
    "name": "Jane Doe",
    "tags": ["lead"]
  }'
Response
{
  "ok": true,
  "contactId": "e3a7…",
  "created": true
}
GET/api/v1/contacts

List contacts

List contacts (newest first), or fetch one by email. Filter by tag or lifecycle stage.

Parameters
email
optional — return the matching contact
tag
optional — filter by a tag
lifecycle
optional — lead | active | customer | churned
limit
optional — 1–200 (default 50)
Request
curl "https://onebubbles.com/api/v1/contacts?lifecycle=customer&limit=50" \
  -H "Authorization: Bearer od_sk_..."
Response
{
  "contacts": [{
    "id": "e3a7…",
    "name": "Jane Doe",
    "email": "jane@acme.com",
    "tags": ["vip"],
    "lifecycleStage": "customer",
    "company": "Acme",
    "blocked": false,
    "lastSeenAt": "2026-07-15T…"
  }]
}
POST/api/v1/messages

Send an inbound message

Inject a message from any custom source into the inbox as the “api” channel. Runs the full pipeline: contact upsert, auto-assignment, AI auto-tagging, agent push, and webhooks.

Parameters
message
required — the message text
externalId
required (or email) — a stable per-user id in your system
name
optional
email
optional
phone
optional
Request
curl -X POST https://onebubbles.com/api/v1/messages \
  -H "Authorization: Bearer od_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "user-123",
    "name": "Jane",
    "email": "jane@acme.com",
    "message": "My order hasn't arrived"
  }'
Response
{
  "ok": true,
  "conversationId": "97…",
  "contactId": "e3…",
  "messageId": "6e…"
}
GET/api/v1/conversations

Read conversations

List conversations (newest first), optionally by contact — or pass conversationId to get a single conversation with its full message transcript.

Parameters
conversationId
optional — return one conversation + transcript
email
optional — filter to a contact
contactId
optional — filter to a contact
status
optional — open | pending | closed
priority
optional — high | normal
limit
optional — 1–100 (default 20)
Request
curl "https://onebubbles.com/api/v1/conversations?email=jane@acme.com" \
  -H "Authorization: Bearer od_sk_..."
Response
{
  "conversations": [{
    "id": "97…",
    "status": "open",
    "channel": "web",
    "tags": ["refund","billing"],
    "priority": "high",
    "language": "en",
    "lastMessagePreview": "Thanks!",
    "rating": null,
    "contact": { "name": "Jane", "email": "jane@acme.com" }
  }]
}
GET/api/v1/stats

Workspace metrics

Summary metrics for building dashboards: conversation counts by status, new conversations in the last 30 days, contact total, and CSAT.

Request
curl https://onebubbles.com/api/v1/stats \
  -H "Authorization: Bearer od_sk_..."
Response
{
  "conversations": {
    "total": 128, "open": 7, "pending": 1,
    "closed": 118, "snoozed": 2, "urgent": 3, "last30d": 40
  },
  "contacts": { "total": 96 },
  "csat": { "average": 4.3, "responseRate": 62, "count": 51 }
}
POST/api/v1/ai/reply

Draft an AI reply

Given a customer message, returns an AI-drafted reply grounded in your knowledge base, plus an escalate flag when a human should step in. Consumes one AI credit.

Parameters
message
required — the customer's message
Request
curl -X POST https://onebubbles.com/api/v1/ai/reply \
  -H "Authorization: Bearer od_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "message": "What are your opening hours?" }'
Response
{
  "reply": "We're open Mon–Fri, 9am–6pm…",
  "escalate": false
}

Webhooks

Subscribe to events in Dashboard → Settings → Webhooks. We POST a signed JSON payload to your URL for each event you pick.

conversation.created

A new conversation started

conversation.assigned

A conversation got an owner

conversation.resolved

A conversation was closed

conversation.rated

A visitor submitted a CSAT rating

contact.created

A new contact was added

message.received

An inbound message arrived

Verifying signatures

Each delivery includes an X-Onebubbles-Signature header: sha256=<hex>, an HMAC-SHA256 of the raw request body using your webhook's signing secret. Recompute it and compare to reject spoofed requests.

// Node.js
import crypto from "crypto";

function verify(rawBody, header, secret) {
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret)
      .update(rawBody).digest("hex");
  return header === expected;
}