Skip to content
Get Started

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 Chat SDK, the Sendblue adapter, and an in-memory state adapter for local development:

Terminal window
npm install chat chat-adapter-sendblue @chat-adapter/state-memory

The 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.

The factory reads credentials from environment variables by default:

VariableRequiredDescription
SENDBLUE_API_KEYYesYour Sendblue API key ID
SENDBLUE_API_SECRETYesYour Sendblue API secret key
SENDBLUE_FROM_NUMBERYesYour Sendblue number in E.164 format
SENDBLUE_WEBHOOK_SECRETNoShared secret for webhook verification
SENDBLUE_STATUS_CALLBACK_URLNoStatus callback URL for outbound message delivery events

You can find your API credentials and assigned Sendblue numbers in the dashboard, or with the CLI:

Terminal window
npm install -g @sendblue/cli
sendblue setup
sendblue show-keys
sendblue lines
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,
});

Create a webhook endpoint in your application and route incoming Sendblue webhook requests to Chat SDK:

app/api/webhooks/sendblue/route.ts
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/sendblue

If 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",
});

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.

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.

FeatureSupport
Inbound messagesYes
Outbound messagesYes
iMessage, SMS, and RCS service filteringYes
Delivery status callbacksYes
Typing indicatorsYes for 1:1 conversations
Tapback reactionsAdd reactions only
Message historyYes, with cursor pagination
Media attachmentsInbound media parsing; outbound media through direct SDK access
Message editingNot supported by iMessage via API
Unsend/deleteNo recipient-side unsend; deleteMessage does not remove delivered messages

By default, process only iMessage traffic. To also accept SMS and RCS inbound messages:

createSendblueAdapter({
allowedServices: ["iMessage", "SMS", "RCS"],
});
  • 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_number stable 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.