Vercel Chat SDK adapter for Linq. Send and receive iMessage / SMS / RCS through real devices via the Linq Partner API.
bun add chat-adapter-linq chatimport { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createLinqAdapter } from "chat-adapter-linq";
const linq = createLinqAdapter({
defaultFrom: "+12025551234",
// apiKey: "...", // or set LINQ_API_KEY env var
// signingSecret: "...", // or set LINQ_WEBHOOK_SECRET env var
});
const chat = new Chat({
userName: "linq-bot",
adapters: { linq },
state: createMemoryState(),
});
chat.onDirectMessage(async (thread, message) => {
await thread.startTyping();
await thread.post(`You said: ${message.text}`);
});
await chat.initialize();
// Wire chat.webhooks.linq to a public HTTPS endpoint that Linq POSTs to.| Variable | Description |
|---|---|
LINQ_API_KEY |
Linq Partner API bearer token (overridden by config.apiKey). |
LINQ_WEBHOOK_SECRET |
HMAC-SHA256 webhook signing secret (overridden by config.signingSecret). |
LINQ_FROM |
E.164 phone number this bot sends from (overridden by config.defaultFrom). |
interface LinqAdapterConfig {
/** Linq Partner API bearer token. */
apiKey: string;
/** HMAC-SHA256 webhook signing secret. Returned only when the subscription is created — store it. */
signingSecret: string;
/** E.164 phone number to send from (e.g. "+12025551234"). */
defaultFrom: string;
/** Override base URL. Default: "https://api.linqapp.com/api/partner". */
baseUrl?: string;
/** Bot username surfaced in handler context. Default: `linq:{defaultFrom}`. */
userName?: string;
/** Custom logger. */
logger?: Logger;
/** Override fetch (testing). */
fetch?: typeof fetch;
/** Reject webhooks older than this many seconds. Default: 300. */
webhookToleranceSec?: number;
}POST /v3/webhook-subscriptions with ?version=2026-02-03 on your target URL. The adapter only parses the 2026-02-03 payload format. The signing secret is returned exactly once — copy it into LINQ_WEBHOOK_SECRET immediately.
- Inbound messages (1:1 routes to
onDirectMessage; group toonSubscribedMessageafter subscribe). - Outbound text, markdown, and Card postables (cards render as fallback text — iMessage has no rich card surface).
- Attachments via URL (≤10MB) or pre-upload (
POST /v3/attachments, ≤100MB). - Reactions: native tapbacks (
love,like,dislike,laugh,emphasize,question) plus arbitrary emoji viacustom_emoji. - Edit (
PATCH /v3/messages/{id}) and delete (DELETE /v3/messages/{id}— Linq-side only, not unsend). - Typing indicator (DMs only; group chats silently no-op).
openDM(handle)reuses an existing 1:1 chat or lazy-creates one on firstpost().
- Buttons, modals, slash commands, scheduled messages — iMessage doesn't have them.
- v2025-01-01 webhook payload format.
@botmentions — iMessage has no syntax for them. UseonDirectMessagefor 1:1 andonNewMessage(/.*/)to opt into group threads.
MIT