Skip to content

Commit c13cb68

Browse files
lingling
authored andcommitted
docs(imessage): document sendCustomizedMiniApp
1 parent 64fcbc2 commit c13cb68

3 files changed

Lines changed: 53 additions & 51 deletions

File tree

docs-src/advanced-kits/imessage/messages.mdx.vel

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Get a `chat.guid` before calling message APIs. `chat.guid` is the server's chat
2222
| Add a message effect | `effect: MessageEffect.*` |
2323
| Format text | `formatting: [...]` |
2424
| Send an attachment | Upload with `im.attachments.upload(...)`, then call `im.messages.sendAttachment(...)` |
25-
| Send a mini app card | `im.messages.sendMiniApp(chat.guid, { url, preview })` |
25+
| Send a card that opens your iMessage extension | `im.messages.sendCustomizedMiniApp(chat.guid, message)` |
2626
| Reply to a message | `replyTo` on `sendText(...)`, `sendAttachment(...)`, or `sendMultipart(...)` |
2727
| Send multipart content | `im.messages.sendMultipart(...)` |
2828
| Add or remove a reaction | `im.messages.setReaction(...)` |
@@ -186,64 +186,66 @@ await im.messages.sendAttachment(chat.guid, audio.attachment.guid, {
186186

187187
## Send Mini App Cards
188188

189-
`sendMiniApp(...)` sends a tappable iMessage card that opens a URL. Use it when you want a custom preview card instead of an automatic link preview.
189+
`sendCustomizedMiniApp(...)` sends a card that, when tapped, opens your iMessage extension and hands it `url`. Use it to launch your own app with a structured payload; for plain link previews, just put the URL in `sendText(...)`.
190+
191+
You need a published iMessage extension on the App Store before you can call this. `appName`, `appStoreId`, `teamId`, and `extensionBundleId` identify that extension so Messages.app can route the tap to it on the recipient's device.
192+
193+
For most cards we recommend an image preview with an overlaid title:
190194

191195
```ts
192-
const sent = await im.messages.sendMiniApp(chat.guid, {
193-
url: "https://photon.codes",
194-
preview: {
195-
title: "Photon",
196-
subtitle: "photon.codes",
197-
body: "Build messaging agents with advanced iMessage support.",
196+
const sent = await im.messages.sendCustomizedMiniApp(chat.guid, {
197+
appName: "MyGame",
198+
appStoreId: 1234567890,
199+
teamId: "ABCDE12345",
200+
extensionBundleId: "com.example.mygame.MessagesExtension",
201+
url: "https://mygame.example.com/level/7",
202+
layout: {
203+
image: await readFile("preview.jpg"),
204+
imageTitle: "Level 7",
198205
},
199206
});
200207

201208
console.log(sent.guid);
202209
```
203210

204-
<Frame>
205-
<img src="/images/advanced-kits/imessage/messages/mini-app-card.avif" alt="Mini app card preview showing a custom web page card" />
206-
</Frame>
207-
208-
The SDK only asks for the destination URL and the preview content shown on the card. The server handles the iMessage app-card details internally.
211+
| Field | Meaning |
212+
|---|---|
213+
| `appName` | Display name of your app. Recipients without your extension installed see this on an App Store install prompt. |
214+
| `appStoreId` | Numeric App Store id, e.g. `1234567890` from `apps.apple.com/app/id1234567890`. Positive integer. |
215+
| `teamId` | 10-character uppercase alphanumeric Apple Team ID. Find it in App Store Connect → Membership. |
216+
| `extensionBundleId` | Bundle identifier of the iMessage extension target inside your app. |
217+
| `url` | Absolute URL delivered to your extension when the recipient taps the card. |
218+
| `layout` | What the card looks like in the conversation, covered in [Card Layout](#card-layout) below. |
209219

210-
Only `url` and `preview.title` are required. Most cards should also set `preview.subtitle`, `preview.body`, and `preview.imageJpeg` when that content is available. To include an image, pass JPEG bytes as `preview.imageJpeg`:
220+
<Note>
221+
This call does not accept `replyTo`, message effects, or `subject`. The only option you can pass in the final argument is `clientMessageId`, used as an idempotency key for job retries.
222+
</Note>
211223

212-
```ts
213-
await im.messages.sendMiniApp(chat.guid, {
214-
url: "https://photon.codes",
215-
preview: {
216-
title: "Photon",
217-
subtitle: "photon.codes",
218-
imageJpeg: await readFile("preview.jpg"),
219-
},
220-
});
221-
```
224+
### Card Layout
222225

223-
| Field | Required | Meaning |
224-
|---|---|---|
225-
| `url` | Yes | URL opened when the recipient taps the card |
226-
| `preview.title` | Yes | Primary text shown on the card |
227-
| `preview.subtitle` | No | Secondary text |
228-
| `preview.body` | No | Supporting text |
229-
| `preview.imageJpeg` | No | JPEG preview image bytes |
230-
| `preview.caption` | No | Additional small label when the card layout has room |
231-
| `preview.footer` | No | Additional footer label when the card layout has room |
232-
| `preview.detail` | No | Additional detail label when the card layout has room |
233-
| `preview.summary` | No | Fallback summary for surfaces that cannot show the full card |
226+
`layout` mirrors Apple's `MSMessageTemplateLayout` — slot names match the Apple field names, so Apple's documentation applies directly.
234227

235-
| Option | Meaning |
228+
| Slot | Where it renders |
236229
|---|---|
237-
| `clientMessageId` | Optional idempotency key for job retries; pass it as `options.clientMessageId` in the final argument |
230+
| `caption` | Top-left, bold. The most prominent text slot. |
231+
| `subcaption` | Below `caption`, on the left. |
232+
| `trailingCaption` | Top-right. |
233+
| `trailingSubcaption` | Below `trailingCaption`, on the right. |
234+
| `image` | JPEG preview image filling the card. |
235+
| `imageTitle` | Overlay text above the image. |
236+
| `imageSubtitle` | Overlay text below `imageTitle`. |
237+
| `summary` | Fallback text for notifications, lock screens, and other surfaces that cannot render the full card. |
238+
239+
The server enforces these rules at send time:
240+
241+
- At least one of `caption`, `subcaption`, `trailingCaption`, `trailingSubcaption`, or `image` must be set. `summary` alone is not enough — it only appears on fallback surfaces.
242+
- `image` and `imageTitle` must be set together. Setting one without the other is rejected.
243+
- `imageSubtitle` requires `image`.
238244

239245
<Warning>
240-
Pass JPEG bytes to `preview.imageJpeg`. If your source image is PNG, WebP, HEIC, or another format, convert it to JPEG before calling `sendMiniApp(...)`.
246+
`layout.image` must be JPEG bytes. The server checks the JPEG SOI marker (`FF D8`) and rejects any other format. Convert PNG, WebP, HEIC, or anything else to JPEG before calling.
241247
</Warning>
242248

243-
<Note>
244-
`sendMiniApp(...)` is its own send operation. It does not upload files first, does not take an attachment GUID, and does not use `replyTo` or message effects.
245-
</Note>
246-
247249
## Reply to a Message
248250

249251
To reply to a message, pass the target message GUID as `replyTo`:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
},
1414
"devDependencies": {
1515
"@antfu/eslint-config": "^4.19.0",
16-
"@photon-ai/advanced-imessage": "^0.9.1",
16+
"@photon-ai/advanced-imessage": "^0.10.0",
1717
"@photon-ai/advanced-imessage-kit": "^1.14.3",
1818
"@photon-ai/imessage-kit": "^2.1.2",
1919
"@photon-ai/whatsapp-business": "^0.1.1",

pnpm-lock.yaml

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)