HTTP endpoint (any database)
ByoTalk replicates natively into PostgreSQL, MySQL/MariaDB and MongoDB. For anything else (SQL Server, Oracle, DynamoDB, Firestore, Cassandra, a data lake…) point ByoTalk at an HTTPS endpoint you run, and store the data wherever you like.
Set it up
- Storage → Database, engine HTTP endpoint, paste your endpoint URL and save. The signing secret (
whsec_…) is shown once; store it in your endpoint's configuration. You can rotate it later from the same page. - Make the endpoint answer
ping(below), then press Test connection. - Press Enable your own database.
A complete example that stores everything in SQLite with no dependencies ships with the SDK repository: examples/http-sink/server.mjs.
The contract
Every request is POST with a JSON body and Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature), signed with your endpoint secret. Verify with chatServer.webhooks.verify(rawBody, headers) from byotalk/server, or any Standard Webhooks library, against the raw body.
type |
Body | Answer |
|---|---|---|
ping |
{} |
200 {"ok": true, "schemaVersion": 1} |
batch |
{schemaVersion, batchId, users[], conversations[], members[], messages[]} |
any 2xx once stored |
read |
{conversationId, seqs[]} |
200 {"messages": [...]} (messages you stored) |
read is used for history older than our buffer: return the messages you stored for those seq values.
Rules
- Idempotent and version-guarded. Batches can be delivered more than once. Store a row only if it is new or its
versionis higher (members:updatedAtnot older). Never let an older version replace a newer one. - Answer only after storing. A non-2xx (or timeout) makes ByoTalk keep the data and retry with backoff; the dashboard shows the sink as lagging or failing and your chat keeps working.
- 401/403 pause the sink until you fix the endpoint and press Test connection.
Message shape
{ "id": "msg_01J9ZN...", "conversationId": "conv_01J9ZK...", "seq": 813, "senderId": "user_123",
"text": "Can you share the invoice?", "replyTo": null, "attachments": [], "metadata": {},
"version": 1, "createdAt": "2026-10-04T10:06:00.120Z", "editedAt": null, "deletedAt": null }
Minimal handler (Node)
import express from "express";
import { ChatServer } from "byotalk/server";
const chatServer = new ChatServer({ secretKey: process.env.BYOTALK_SECRET_KEY! });
const app = express();
app.post("/byotalk/sink", express.raw({ type: "application/json" }), async (req, res) => {
let body: any;
try {
// Third argument: the endpoint's signing secret (whsec_…), not your webhook endpoints' secret.
body = chatServer.webhooks.verify(req.body, req.headers, process.env.BYOTALK_SINK_SECRET);
} catch {
return res.sendStatus(401);
}
if (body.type === "ping") return res.json({ ok: true, schemaVersion: 1 });
if (body.type === "batch") {
await store(body); // upsert users, conversations, members, messages by id + version
return res.sendStatus(204);
}
if (body.type === "read") return res.json({ messages: await load(body.conversationId, body.seqs) });
res.sendStatus(400);
});