@interactions-hq/skyline is the unified messaging SDK for TypeScript. Write your logic once, deliver it across every platform — iMessage, WhatsApp, WhatsApp Business, or your terminal for local development.

Installation

@interactions-hq/skyline is the batteries-included package — it bundles the runtime and every official provider.
npm install @interactions-hq/skyline
pnpm add @interactions-hq/skyline
yarn add @interactions-hq/skyline
bun add @interactions-hq/skyline
Requires TypeScript 5 or later (TypeScript 6 is also supported). Node.js 18+ and Bun 1.0+ are supported runtimes.

Core concepts

Skyline is built around four primitives:
PrimitiveWhat it represents
MessageAn incoming piece of content — text, attachments, or structured data — from any platform.
SpaceA conversation context. A DM, a group chat, a terminal session. You send messages into a space.
UserA participant on a platform, identified by a platform-specific ID.
ProviderA platform adapter (iMessage, WhatsApp, terminal, …) that translates platform-specific protocols into Skyline’s unified interface.
Every message arrives as a [Space, Message] tuple. The space gives you the ability to respond; the message gives you the content and metadata.

Quickstart

Get your credentials

Find your SKYLINE_PROJECT_ID and SKYLINE_PROJECT_SECRET in your project Settings on the dashboard, or from your hackathon organizer. Cloud mode uses https://api.interactions.co.in — no base URL to configure.

Run your first app (terminal — no credentials)

The terminal provider works without credentials. Use it to validate your agent loop before connecting iMessage:
import { Skyline, terminal } from "@interactions-hq/skyline";

const app = await Skyline({
  providers: [terminal.config({ prompt: "you> " })],
});

console.log("Type a message. Ctrl+C to exit.");

for await (const [space, message] of app.messages) {
  if (message.content.type !== "text") continue;
  console.log(`[terminal] ${message.sender.id}: ${message.content.text}`);
  await space.send(`echo: ${message.content.text}`);
}
Run it:
bun run agent.ts

Run your first iMessage app (cloud)

import { Skyline, imessage } from "@interactions-hq/skyline";

const app = await Skyline({
  projectId: process.env.SKYLINE_PROJECT_ID!,
  projectSecret: process.env.SKYLINE_PROJECT_SECRET!,
  providers: [imessage.config()],
});

for await (const [space, message] of app.messages) {
  if (message.isFromMe || message.content.type !== "text") continue;
  console.log(`[imessage] ${message.sender.id}: ${message.content.text}`);
  await space.send("Hello from Skyline.");
}

The app instance

Skyline() returns a SkylineApp — an object with a merged inbound feed and platform-specific send surfaces.
app.messages                 // AsyncIterable<[Space, Message]>
app.space("+1555…")          // open a conversation
app.on("reaction", handler)  // non-message signals
app.ready                    // Set of connected line handles
await app.close()            // graceful shutdown
Non-message signals (reactions, typing, read receipts, edits, unsends, send errors) arrive through app.on(event, handler) rather than the message feed.

Multi-platform in three lines

Combine providers to receive and send across platforms simultaneously:
import { Skyline, imessage, terminal, whatsapp, whatsappBusiness } from "@interactions-hq/skyline";

const app = await Skyline({
  projectId: process.env.SKYLINE_PROJECT_ID!,
  projectSecret: process.env.SKYLINE_PROJECT_SECRET!,
  providers: [
    imessage.config(),
    whatsapp.config(),
    whatsappBusiness.config(),
    terminal.config(),
  ],
});

for await (const [space, message] of app.messages) {
  if (message.content.type !== "text") continue;
  await space.typing(true);
  await space.send(`Got it on ${message.platform}.`);
  await space.typing(false);
}
Messages from every provider merge into the single app.messages stream. The message.platform field identifies the source.

Environment variables

VariableDescription
SKYLINE_PROJECT_IDYour project UUID
SKYLINE_PROJECT_SECRETYour project secret (sk_…)
Store credentials in a secrets manager or .env file — never commit them to source control.

Next steps

Spaces and users

Send, react, reply, and type in a space.

iMessage provider

Connect to managed iMessage lines.