Chat SDK adapter
Connect Vercel Chat SDK bots to iMessage, SMS, and RCS through Sendblue.
The Sendblue Chat SDK adapter connects Chat SDK bots to iMessage, SMS, and RCS. Use it when you want one bot implementation to handle Sendblue conversations alongside Slack, Teams, Discord, Telegram, email, or other Chat SDK adapters.
Install
Section titled “Install”Install Chat SDK, the Sendblue adapter, and an in-memory state adapter for local development:
npm install chat chat-adapter-sendblue @chat-adapter/state-memoryThe Sendblue adapter uses the official Sendblue TypeScript SDK under the hood. For production, use a persistent state adapter such as @chat-adapter/state-redis instead of in-memory state.
Environment Variables
Section titled “Environment Variables”The factory reads credentials from environment variables by default:
| Variable | Required | Description |
|---|---|---|
SENDBLUE_API_KEY | Yes | Your Sendblue API key ID |
SENDBLUE_API_SECRET | Yes | Your Sendblue API secret key |
SENDBLUE_FROM_NUMBER | Yes | Your Sendblue number in E.164 format |
SENDBLUE_WEBHOOK_SECRET | No | Shared secret for webhook verification |
SENDBLUE_STATUS_CALLBACK_URL | No | Status callback URL for outbound message delivery events |
You can find your API credentials and assigned Sendblue numbers in the dashboard, or with the CLI:
npm install -g @sendblue/clisendblue setupsendblue show-keyssendblue linesCreate a Bot
Section titled “Create a Bot”import { Chat } from "chat";import { createSendblueAdapter } from "chat-adapter-sendblue";import { createMemoryState } from "@chat-adapter/state-memory";
const chat = new Chat({ userName: "imessage-bot", adapters: { sendblue: createSendblueAdapter(), }, state: createMemoryState(),});
chat.onDirectMessage(async (thread, message) => { await thread.post(`Got it: ${message.text}`);});You can also pass credentials explicitly:
import { createSendblueAdapter } from "chat-adapter-sendblue";
const sendblue = createSendblueAdapter({ apiKey: process.env.SENDBLUE_API_KEY, apiSecret: process.env.SENDBLUE_API_SECRET, defaultFromNumber: "+15551234567", webhookSecret: process.env.SENDBLUE_WEBHOOK_SECRET,});Handle Webhooks
Section titled “Handle Webhooks”Create a webhook endpoint in your application and route incoming Sendblue webhook requests to Chat SDK:
import { chat } from "@/lib/chat";
export async function POST(request: Request) { await chat.initialize(); return chat.webhooks.sendblue(request);}Then configure your Sendblue receive webhook to point at that endpoint:
https://your-app.com/api/webhooks/sendblueIf you set SENDBLUE_WEBHOOK_SECRET, Sendblue should send the same secret with webhook requests. The adapter verifies the sb-signing-secret header by default. You can override the header name:
createSendblueAdapter({ webhookSecret: process.env.SENDBLUE_WEBHOOK_SECRET, webhookSecretHeader: "x-custom-header",});Send Messages
Section titled “Send Messages”Use normal Chat SDK thread methods:
await thread.post("Hello from Sendblue via Chat SDK.");The adapter sends outbound messages through Sendblue with iMessage-first delivery and automatic SMS/RCS fallback where available.
Use Sendblue Features Directly
Section titled “Use Sendblue Features Directly”For Sendblue APIs that are outside the standard Chat SDK adapter interface, access the underlying Sendblue SDK:
import type { SendblueAdapter } from "chat-adapter-sendblue";
const adapter = chat.getAdapter("sendblue") as SendblueAdapter;const sdk = adapter.getSdk();
await sdk.messages.send({ number: "+15551234567", from_number: process.env.SENDBLUE_FROM_NUMBER!, content: "Here is a photo.", media_url: "https://example.com/photo.jpg",});This is useful for media messages, contact operations, webhook management, groups, and any new Sendblue API feature before it has a first-class Chat SDK abstraction.
Supported Features
Section titled “Supported Features”| Feature | Support |
|---|---|
| Inbound messages | Yes |
| Outbound messages | Yes |
| iMessage, SMS, and RCS service filtering | Yes |
| Delivery status callbacks | Yes |
| Typing indicators | Yes for 1:1 conversations |
| Tapback reactions | Add reactions only |
| Message history | Yes, with cursor pagination |
| Media attachments | Inbound media parsing; outbound media through direct SDK access |
| Message editing | Not supported by iMessage via API |
| Unsend/delete | No recipient-side unsend; deleteMessage does not remove delivered messages |
Accept SMS and RCS
Section titled “Accept SMS and RCS”By default, process only iMessage traffic. To also accept SMS and RCS inbound messages:
createSendblueAdapter({ allowedServices: ["iMessage", "SMS", "RCS"],});Production Notes
Section titled “Production Notes”- Use a persistent Chat SDK state adapter in production so subscribed threads and conversation state survive deploys.
- Store Sendblue API keys in environment variables or your host’s secret manager.
- Persist inbound media URLs if you need long-term access. Sendblue media URLs can expire.
- Keep your
from_numberstable per contact so end users always see the same Sendblue line. - Use the Lookup API before high-volume sends if you need to know whether a recipient supports iMessage.