Skip to main content
Use this page to choose an iMessage package and understand how Spectrum routes iMessage conversations. Cloud and local iMessage are separate platforms; each is selected by its provider import, not by a config flag. A macOS application can register both when it needs both transports.

Provider packages

Authenticates with Spectrum Cloud and connects to managed iMessage infrastructure via gRPC. Supports sending, receiving, typing indicators, reactions, and replies. Group creation and inbound group-change events require a dedicated line.With automatic discovery, tokens are renewed at 80% of their TTL. This requires projectId and projectSecret on the Spectrum() call:
Spectrum discovers all cloud lines owned by the project and renews their tokens automatically. For advanced routing, you can instead provide a subset of the project’s cloud clients:
Explicit clients let you control which cloud-owned lines this SDK instance subscribes to. They still use the cloud package. Their tokens are not renewed by the SDK, so you are responsible for keeping them current; most applications should use automatic discovery.

Line model

Cloud mode routes your messages through phone numbers, also called lines, provisioned by Spectrum. Which lines you get depends on your plan, and the difference is mostly invisible to end users. DM delivery is identical across both cloud line models. The developer-facing differences are sender-number allocation and group support.
Shared-pool mode does not create group chats and does not subscribe to iMessage’s group-event stream. Changes such as adding or removing a member, leaving, renaming the chat, or changing its avatar will not appear on app.messages. Use a Business dedicated line when your integration depends on group workflows.

Auto-scale

When traffic to a dedicated line approaches its per-line capacity, Spectrum can automatically provision an additional line so deliverability isn’t affected. Auto-scale is an opt-in feature on the Business plan. Enable it in your project settings if you’d rather not get paged when a line saturates.

When line changes reach a running app

The SDK learns about your lines from the credentials it mints, and it re-mints them on a schedule — so a line provisioned (or deprovisioned) while your app is running is picked up at the next token renewal, not immediately. Until that renewal lands, a newly provisioned line is invisible to the process: it receives no inbound messages, and space.create() cannot route through it. Messages sent to it in that window are not delayed, they are not delivered to your app at all. Restart the process if you need a new line to take effect right away.
On dedicated lines, two routing behaviors change the moment a second line appears, whether that happens at startup or mid-run:
  • space.get(chatGuid) starts requiring params.phone. With exactly one dedicated line the SDK can infer it; with two or more it cannot.
  • space.create() without an explicit phone starts picking at random from the larger set.
Neither applies to shared-pool mode, which always routes through a single shared identity.
These are Spectrum Cloud features. With @spectrum-ts/imessage-local, you provide the Messages account on the Mac and managed-line concepts do not apply.

Quotas

Default per-server and per-line quotas apply. Contact [email protected] for an increase.
  • 5,000 messages per server per day. Counts every message your instance sends across all chats. Additional sends are rejected until the window resets.
  • 50 new conversations initiated per line per day. A “new conversation” is the first message your line sends to a recipient it has never messaged before. Replies within existing conversations don’t count.

Space types

iMessage spaces carry a type field, either "dm" or "group", and a phone field indicating which phone number the conversation is routed through. Both are accessible through narrowing:

User properties

iMessage users carry optional platform-specific fields when resolved through narrowing. These are available when the platform has sender details for the user:
These fields are also available on message.sender after narrowing:

Creating conversations

Resolve users by phone number or email, then create a space with space.create(...):
To look up an existing conversation by its chat GUID, use space.get(id):
The first form works in shared-pool mode and on a single dedicated line, where the SDK can infer the line. With multiple dedicated lines it throws — pass params.phone, as covered in per-phone routing. DM creation works with the cloud package. The local package can construct a deterministic DM reference, but it cannot create a group because the local Messages database does not expose chat creation. Group creation requires a dedicated line. In shared-pool mode, passing multiple users to space.create() throws an . You can use space.get(chatGuid) to reference an existing group, but shared mode still does not receive membership or metadata changes from the group-event stream.

Per-phone routing

If your account has multiple dedicated phone numbers, you can pin a conversation to a specific line by passing phone as a space parameter:
When omitted, Spectrum picks a phone at random from the available dedicated lines. All subsequent actions on that space route through the chosen number, including sending, typing, replies, edits, reactions, unsends, and lookups.
Per-phone routing applies to dedicated lines on the Business plan only. On shared-pool plans the phone parameter is ignored because all conversations route through the shared pool automatically.