birdcash-chat-sdk-alpha
v1.0.21
Published
TypeScript SDK for sending messages through the chat API (text, images, files, miniapp cards, streaming).
Downloads
3,428
Readme
birdcash-chat-sdk
A small TypeScript SDK for sending messages through the chat API. It wraps the
/v1/chat/message/:msgID/reply and /v1/upload/* endpoints so you can send
text, images, files, miniapp cards, web-link previews, typing status, and
streaming (typewriter) responses.
Install
npm install birdcash-chat-sdk-alphaQuick start
import { ChatClient } from "birdcash-chat-sdk-alpha";
const chat = new ChatClient({
token: "<bearer-token>",
// baseUrl: 'https://your-chat-api', // optional, defaults to the hosted API
// logger: console, // optional, omit for silence
});
// …or exchange OAuth client credentials for a token automatically:
const chat2 = await ChatClient.fromCredentials({ clientId, clientSecret });
// Send text (string = one element; array = one request, one element per item)
await chat.sendMessage(msgID, "Hello there!");
await chat.sendMessage(msgID, ["First line", "Second line"]);Options
| Option | Type | Default | Description |
| --------- | -------------- | --------------- | ------------------------------------------ |
| token | string | — (required) | Bearer token sent with every request. |
| baseUrl | string | hosted endpoint | Base URL of the chat API. |
| logger | Logger | undefined | Pass console to log; omit for no output. |
| fetch | typeof fetch | global fetch | Custom fetch (Node < 18, tests, etc.). |
Sending messages
// Images: upload first, then send by upload_id
const { upload_id } = await chat.uploadImage(bytes, "photo.png", "image/png");
await chat.sendImageMessage(msgID, [upload_id]);
// Files
const file = await chat.uploadFile(bytes, "report.pdf", "application/pdf");
await chat.sendFileMessage(msgID, [file.upload_id]);
// Sound (uploaded via uploadFile; duration in seconds)
const audio = await chat.uploadFile(bytes, "clip.m4a", "audio/m4a");
await chat.sendSoundMessage(msgID, audio.upload_id, 12);
// Sticker (by pack + sticker identifiers)
await chat.sendStickerMessage(msgID, "weather", "Cloud with lightning");
// Typing indicator (best effort — never throws)
await chat.sendTypingStatus(msgID, true);
// Miniapp card
await chat.sendMiniAppMessage(msgID, {
appID: "app123",
title: "Open the app",
path: "/home",
imageURL: "https://…/cover.png",
});
// Web link preview
await chat.sendWebLinkMessage(msgID, "https://example.com");
// Choices prompt (ai_choice_prompt) — returns the new message id
const choiceMsgID = await chat.sendChoicesMessage(msgID, {
prompt: "Which shipping option do you want?",
choices: [
{ id: "standard", label: "Standard (5–7 days)" },
{ id: "express", label: "Express (2 days)" },
],
// selectionMode: 'single', // optional, defaults to 'single'
});
// Official-account tweet card — returns the new message id
await chat.sendOfficialAccountMessage(msgID, {
title: "Release notes",
content: "What shipped this week…",
link: "https://example.com/blog",
version: 1,
});Editing & deleting
Send with a known replyMsgID, then edit that message in place (or delete it):
const editable = crypto.randomUUID();
await chat.sendMessage(msgID, "Working on it…", editable);
await chat.editTextMessage(editable, "Done ✅");
// Stickers and images edit in place too
await chat.sendStickerMessage(msgID, "capoo", "capoo_1", editable);
await chat.editStickerMessage(editable, "capoo", "capoo_2");
await chat.editImageMessage(imgMsgID, [upload_id]);
// Delete a message you sent
await chat.deleteMessage(editable);Streaming (typewriter effect)
// Recommended: reply to a user message with throttled chatbotPlugin updates
const streamMsgID = await chat.sendStreamMessage(parentMsgID, "A long streamed reply…");
// Low-level: drive each flush yourself (POST parent /reply first, then stream id)
await chat.postStreamMessageUpdate(parentMsgID, streamMsgID, ["Hello "], false);
await chat.postStreamMessageUpdate(streamMsgID, streamMsgID, ["Hello world!"], true);
// Legacy: chunk via POST then PATCH on the same message id
await chat.streamMessage(msgID, "A long streamed reply…");
await chat.sendStreamingChunk(msgID, ["Hel"], false, true);
await chat.sendStreamingChunk(msgID, ["Hel", "lo"], true, false);OAuth
If you don't already have a bearer token, exchange client credentials for one:
import { getAccessToken } from "birdcash-chat-sdk-alpha";
const token = await getAccessToken({
clientId,
clientSecret,
scope: "chat:write uploads:write", // default
});
// token.access_token, token.expires_in, …Verifying webhooks
Validate incoming webhook requests before trusting them. The signature scheme
matches the server: HMAC-SHA256(secret, "${timestamp}.${rawBody}"), sent in the
X-Webhook-Signature (optionally sha256=-prefixed) and X-Webhook-Timestamp
headers. Pass the raw body text, read before JSON-parsing.
import { verifyWebhookSignature } from "birdcash-chat-sdk-alpha";
const raw = await request.text();
const { valid, error } = await verifyWebhookSignature(
request,
env.WEBHOOK_SECRET,
raw,
);
if (!valid) return new Response(error ?? "bad signature", { status: 401 });
const event = JSON.parse(raw);
// …handle eventIt rejects missing headers, bad signatures (constant-time compare), and stale
timestamps (replay protection, default 300s — override with { toleranceSec }).
Pass { logger: console } for diagnostics.
Lower-level exports
The element builders and helpers are also exported if you want to assemble requests yourself:
import {
ElemType,
textElem,
imageElem,
splitIntoChunks,
toBase64,
} from "birdcash-chat-sdk-alpha";