Skip to main content
Use these provider-specific helpers after you have configured the iMessage provider.

Message effects

iMessage supports bubble effects, which animate the sent message bubble, and screen effects, which play a full-screen animation on receive. Wrap any content with effect():
import { attachment } from "spectrum-ts";
import { effect, imessage } from "spectrum-ts/providers/imessage";

await space.send(effect("Happy birthday!", imessage.effect.message.celebration));
await space.send(effect(attachment("/path/to/photo.jpg"), imessage.effect.message.confetti));
The wrapped content can be a string, markdown(...), or any attachment(...). Effects only apply on iMessage. Other platforms see the inner content unchanged.
ConstantValue
imessage.effect.message.slam"com.apple.MobileSMS.expressivesend.impact"
imessage.effect.message.loud"com.apple.MobileSMS.expressivesend.loud"
imessage.effect.message.gentle"com.apple.MobileSMS.expressivesend.gentle"
imessage.effect.message.invisible"com.apple.MobileSMS.expressivesend.invisibleink"
ConstantValue
imessage.effect.message.confetti"com.apple.messages.effect.CKConfettiEffect"
imessage.effect.message.fireworks"com.apple.messages.effect.CKFireworksEffect"
imessage.effect.message.balloons"com.apple.messages.effect.CKBalloonEffect"
imessage.effect.message.heart"com.apple.messages.effect.CKHeartEffect"
imessage.effect.message.lasers"com.apple.messages.effect.CKLasersEffect"
imessage.effect.message.celebration"com.apple.messages.effect.CKHappyBirthdayEffect"
imessage.effect.message.sparkles"com.apple.messages.effect.CKSparklesEffect"
imessage.effect.message.spotlight"com.apple.messages.effect.CKSpotlightEffect"
imessage.effect.message.echo"com.apple.messages.effect.CKEchoEffect"

Chat renaming

Rename a group chat using space.rename() or the canonical rename() content builder:
import { rename } from "spectrum-ts";

// Sugar
await space.rename("Book Club");

// Canonical
await space.send(rename("Book Club"));
Renaming requires cloud or dedicated mode and only works on group chats. In local mode or on a DM, rename() throws an UnsupportedError.

Group avatars

Set or clear the group chat icon using space.avatar() or the canonical avatar() content builder:
import { avatar } from "spectrum-ts";

// Sugar - set from a file path
await space.avatar("./icon.png");

// Sugar - clear the current avatar
await space.avatar("clear");

// Canonical
await space.send(avatar("./icon.png"));
Group avatars require cloud or dedicated mode and only work on group chats. In local mode or on a DM, avatar() throws an UnsupportedError.

Chat backgrounds

Set or clear the chat background image. Import background from the iMessage provider and use the sugar method on a narrowed space:
import { background, imessage } from "spectrum-ts/providers/imessage";

const im = imessage(space);

// Set from a file path - MIME type inferred from the extension
await im.background("./wallpaper.jpg");

// Set from a buffer - mimeType is required
await im.background(buffer, { mimeType: "image/jpeg" });

// Clear the current background
await im.background("clear");
space.background(...) is sugar for space.send(background(...)). The canonical form works on any space reference:
await space.send(background("./wallpaper.jpg"));
await space.send(background("clear"));
After a successful set, the background usually syncs to other users’ devices within 30s. The background asset is uploaded to iCloud and then distributed to the other members of the conversation. Display time is not a hard SLA: network state, iCloud state, and the Messages client state can all affect when the UI appears.
StageWhat happens
Before background(...) resolvesThe provider waits until the background asset reaches a distributable state.
After background(...) resolvesThe conversation has accepted the background change; iCloud distributes the asset to other members.
Other members’ devicesThe background appears after the device receives the iCloud distribution.
Background UI may not appear in these cases:
CaseResult
The recipient’s network, iCloud, or Messages state is unhealthyThe background may appear late, often after reopening Messages.
A group member has never spoken, interacted, or is treated by the system as unknown or untrustedApple may not show the background UI to that member.
The second case is an Apple Messages display limit, not a Spectrum option. If one group member never sees the background, have that member send a message in the group, mark the sender as known, or reopen Messages before retrying the background change.
Chat backgrounds require cloud or dedicated mode. In local mode, background() throws an UnsupportedError.
The string "clear" is a reserved sentinel. If you have a file literally named clear with no extension, pass "./clear" or load it as a Buffer.

Mini-app cards

Send a customized iMessage mini-app card: a remote-only rich bubble that shows app metadata, a deep link, and a visual layout. Import customizedMiniApp from the iMessage provider:
import { customizedMiniApp } from "spectrum-ts/providers/imessage";

const sent = await space.send(customizedMiniApp({
  appName: "My App",
  extensionBundleId: "com.example.myapp.imessage",
  teamId: "ABCDE12345",
  url: "https://example.com/deep-link",
  layout: {
    caption: "Check this out",
    subcaption: "Tap to open",
  },
}));
space.send(customizedMiniApp(...)) returns the sent message record. Unlike background and read, mini-app cards are real outbound messages. appStoreId is optional. Omit it to send a card whose extension isn’t published on the App Store. When set, recipients without the extension are directed to its App Store entry.
FieldTypeDescription
appNamestringDisplay name of the owning app, shown by Messages fallback UI.
appStoreIdnumber (optional)Apple App Store numeric ID. Omit for unpublished extensions.
extensionBundleIdstringBundle identifier of the iMessage extension target.
layoutCustomizedMiniAppLayoutVisible card layout. See below.
teamIdstring10-character uppercase alphanumeric Apple Team ID.
urlstringAbsolute URL delivered to the installed extension on tap.
FieldTypeDescription
captionstring (optional)Primary caption text.
subcaptionstring (optional)Secondary caption text.
trailingCaptionstring (optional)Right-aligned primary text.
trailingSubcaptionstring (optional)Right-aligned secondary text.
imageUint8Array (optional)Image data for the card. Must be set with imageTitle.
imageTitlestring (optional)Title for the image. Must be set with image.
imageSubtitlestring (optional)Subtitle for the image. Requires image.
summarystring (optional)Fallback text for surfaces that can’t render the card, such as notifications and lock screen.
At least one of caption, subcaption, trailingCaption, trailingSubcaption, or image must be set.
Mini-app cards require cloud or dedicated mode. In local mode, customizedMiniApp() throws an UnsupportedError.

Native contact card sharing

Share the bot account’s own iMessage contact card directly in a chat. Use nativeContactCard() to build the content, or the space.shareContactCard() sugar method on a narrowed iMessage space:
import { imessage } from "spectrum-ts/providers/imessage";

const im = imessage(space);
await im.shareContactCard();
This shares the bot’s own contact card as it appears in iMessage. Recipients can tap it to save the contact. Use it in onboarding flows where you want users to add your bot to their contacts.
Native contact card sharing requires cloud or dedicated mode. In local mode, nativeContactCard() throws an UnsupportedError.

Fetching attachments

Retrieve an attachment by its iMessage GUID using getAttachment on the narrowed platform instance. The returned Attachment is lazy. .read() and .stream() each trigger an independent download, so cache .read() if you need the bytes more than once.
import { imessage } from "spectrum-ts/providers/imessage";

const im = imessage(app);
const att = await im.getAttachment("p:0/GUID");

if (att) {
  console.log(att.name, att.mimeType, att.size);
  const bytes = await att.read();
}
In multi-phone mode, pass the phone number as the second argument to route the request through the correct instance:
const att = await im.getAttachment("p:0/GUID", "+15559999999");
When only one phone is configured, or in shared-pool mode, the phone parameter is optional.
getAttachment requires cloud or dedicated mode. In local mode it throws an UnsupportedError.

Tapback constants

iMessage uses a fixed set of tapback reactions. The imessage object exposes them as constants:
ConstantValue
imessage.tapbacks.love"love"
imessage.tapbacks.like"like"
imessage.tapbacks.dislike"dislike"
imessage.tapbacks.laugh"laugh"
imessage.tapbacks.emphasize"emphasize"
imessage.tapbacks.question"question"
import { imessage } from "spectrum-ts/providers/imessage";

await message.react(imessage.tapbacks.laugh);
See Reactions and replies for the cross-platform reaction model.