Linq
Bots that text over iMessage and SMS through Linq — DMs and group chats, media both ways, and native tapback reactions, mapping Linq chats to the Chat SDK thread/message/reaction model.
Install
pnpm add @linqapp/chat-sdk-adapterQuick start
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createLinqAdapter } from "@linqapp/chat-sdk-adapter";
export const bot = new Chat({
userName: "Linq Bot",
adapters: {
linq: createLinqAdapter({
apiKey: process.env.LINQ_API_KEY,
signingSecret: process.env.LINQ_WEBHOOK_SECRET,
}),
},
state: createMemoryState(),
});
bot.onDirectMessage(async (thread, message) => {
await thread.subscribe();
await thread.post(`You said: ${message.text}`);
});
bot.onReaction(["thumbs_up"], async (event) => {
await event.thread.post("Appreciate the tapback 🫡");
});The adapter maps a Linq chat to a Chat SDK thread, a text to a message, and an iMessage tapback to a reaction, so the rest of the Chat SDK API (subscriptions, handlers, posts, reactions) works exactly the same as with any other adapter.
Configuration
Prop
Type
Platform setup
- Create a Linq account and copy your API key. For testing, the Linq CLI provisions a sandbox number:
linq signup --phone <your cell>. - Create a webhook subscription pointing at the route that forwards to
bot.webhooks.linq, and subscribe to:message.receivedreaction.addedreaction.removed
- Copy the signing secret returned by the subscription into
signingSecret.
Other event types are acknowledged with a 200 and ignored.
Webhook events
| Event | Role |
|---|---|
message.received | Drives Chat SDK message processing |
reaction.added | Drives reaction handlers |
reaction.removed | Drives reaction handlers |
import { after } from "next/server";
import { bot } from "@/lib/bot";
export const runtime = "nodejs"; // raw body + crypto; not edge
export async function POST(request: Request) {
return bot.webhooks.linq(request, {
waitUntil: (task) => after(() => task),
});
}Requests are verified with HMAC-SHA256 and a replay-window check before dispatch; an invalid or stale signature returns 401. Passing waitUntil lets work continue after the response is sent in serverless environments like Vercel.
Thread IDs
Thread IDs are stable and always take the form linq:{chatId}, so a conversation maps to the same Chat SDK thread whether it first arrives via webhook or API.
encodeThreadId
adapter.encodeThreadId(data: { chatId: string; isGroup?: boolean }): string;const encoded = adapter.encodeThreadId({ chatId: "3caaf1a0-ef9f-46e0-8c22-31e82c8514dc" });
// "linq:3caaf1a0-ef9f-46e0-8c22-31e82c8514dc"decodeThreadId
adapter.decodeThreadId(threadId: string): { chatId: string; isGroup?: boolean };Group vs. DM identity is tracked internally from webhook payloads; legacy linq:{chatId}:group / linq:{chatId}:dm IDs still decode.
Message format
Outbound markdown is rendered to plain text — Linq delivers message text as-is (iMessage has no markdown). Inbound text is parsed to the Chat SDK AST, with URLs surfaced as link previews.
Reactions
Standard iMessage tapbacks map to normalized Chat SDK emoji in both directions:
| Linq tapback | Chat SDK emoji |
|---|---|
like | thumbs_up |
dislike | thumbs_down |
love | heart |
laugh | laugh |
emphasize | exclamation |
question | question |
Custom emoji reactions pass through the default emoji resolver (e.g. 👍 → thumbs_up), falling back to the raw emoji for anything unmapped. Sticker reactions have no Chat SDK equivalent and are skipped.
Attachments
Inbound media (images, audio, files) arrives as Chat SDK attachments with downloadable data. Outbound attachments and files are sent as Linq media parts: a public HTTPS URL under 10 MB is sent by reference, while raw bytes, non-HTTPS URLs, and files up to 100 MB are pre-uploaded. Messages can be media-only.
Examples
- Adapter source and README
- Example app — one AI bot running across Linq, Telegram, and WhatsApp from a single set of handlers.
Feature support
Messaging
| Feature | Supported |
|---|---|
| Post message | |
| Edit message | Text, first part |
| Delete message | |
| File uploads | Images, audio, files |
| Streaming | Buffered |
| Scheduled messages |
Rich content
| Feature | Supported |
|---|---|
| Card format | |
| Buttons | |
| Link buttons | |
| Select menus | |
| Tables | |
| Fields | |
| Images in cards | |
| Modals |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | |
| Mentions | |
| Add reactions | Tapbacks |
| Remove reactions | Tapbacks |
| Typing indicator | DMs only |
| DMs | |
| Ephemeral messages | |
| User lookup | |
| Parent subject | |
| Native client | |
| Custom API endpoint |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | |
| Fetch single message | |
| Fetch thread info | |
| Fetch channel messages | |
| List threads | |
| Fetch channel info | |
| Post channel message |