Overview
When you send a URL in iMessage, it can automatically generate a rich preview with an image, title, and description. The SDK supports this feature through the richLink parameter.
Basic Rich Link
Send a message with a rich link preview:
import { createSDK , handleError } from "./utils" ;
const CHAT_GUID = process . env . CHAT_GUID || "any;-;+1234567890" ;
async function main () {
const sdk = createSDK ();
sdk . on ( "ready" , async () => {
try {
const message = await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: "https://photon.codes/" ,
richLink: true ,
});
console . log ( `sent rich link: ${ message . guid } ` );
console . log ( `balloonBundleId: ${ message . balloonBundleId } ` );
console . log ( ` ${ new Date ( message . dateCreated ). toLocaleString () } ` );
} catch ( error ) {
handleError ( error , "Failed to send rich link message" );
}
await sdk . close ();
process . exit ( 0 );
});
await sdk . connect ();
}
main (). catch ( console . error );
How It Works
Enable Rich Link
Set richLink: true in your sendMessage call
Include URL
The message text must contain a valid URL
Preview Generation
iMessage fetches the URL’s metadata (title, description, image) and generates a preview
Display
The recipient sees a rich preview card with the link’s content
Parameters
The GUID of the chat to send to
The URL to send (must be a valid HTTP/HTTPS URL)
Set to true to enable rich link preview generation
Multiple Rich Links
Send several rich links in sequence:
const urls = [
"https://photon.codes/" ,
"https://github.com/" ,
"https://apple.com/" ,
];
sdk . on ( "ready" , async () => {
for ( const url of urls ) {
const message = await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: url ,
richLink: true ,
});
console . log ( `Sent: ${ url } ` );
// Wait between sends
await new Promise ( resolve => setTimeout ( resolve , 2000 ));
}
await sdk . close ();
});
URL with Additional Text
You can include text along with the URL:
const message = await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: "Check this out! https://photon.codes/" ,
richLink: true ,
});
The rich preview will be generated for the URL, while the rest of the text appears as normal message text.
Advanced Examples
Link Sharing Bot
sdk . on ( "new-message" , async ( message ) => {
if ( message . isFromMe ) return ;
const text = message . text ?. toLowerCase () || "" ;
const chat = message . chats ?.[ 0 ];
if ( ! chat ) return ;
// Respond to requests with relevant links
let url = "" ;
if ( text . includes ( "documentation" ) || text . includes ( "docs" )) {
url = "https://photon.codes/docs" ;
} else if ( text . includes ( "github" )) {
url = "https://github.com/" ;
} else if ( text . includes ( "news" )) {
url = "https://news.ycombinator.com/" ;
}
if ( url ) {
await sdk . messages . sendMessage ({
chatGuid: chat . guid ,
message: url ,
richLink: true ,
});
}
});
URL Expander
Detect short URLs and send rich previews:
sdk . on ( "new-message" , async ( message ) => {
if ( message . isFromMe ) return ;
const chat = message . chats ?.[ 0 ];
if ( ! chat ) return ;
// Find URLs in message
const urlRegex = / ( https ? : \/\/ [ ^ \s ] + ) / g ;
const urls = message . text ?. match ( urlRegex );
if ( urls && urls . length > 0 ) {
for ( const url of urls ) {
// Check if it's a short URL
if ( url . includes ( "bit.ly" ) || url . includes ( "t.co" ) || url . includes ( "tinyurl.com" )) {
// Send as rich link
await sdk . messages . sendMessage ({
chatGuid: chat . guid ,
message: `Full preview: ${ url } ` ,
richLink: true ,
});
}
}
}
});
Content Recommendations
const recommendations = {
"tech" : [
"https://techcrunch.com/" ,
"https://theverge.com/" ,
"https://arstechnica.com/" ,
],
"news" : [
"https://news.ycombinator.com/" ,
"https://reddit.com/r/news" ,
],
"dev" : [
"https://github.com/trending" ,
"https://stackoverflow.com/" ,
],
};
sdk . on ( "new-message" , async ( message ) => {
if ( message . isFromMe ) return ;
const text = message . text ?. toLowerCase () || "" ;
const chat = message . chats ?.[ 0 ];
if ( ! chat ) return ;
for ( const [ category , urls ] of Object . entries ( recommendations )) {
if ( text . includes ( category )) {
const randomUrl = urls [ Math . floor ( Math . random () * urls . length )];
await sdk . messages . sendMessage ({
chatGuid: chat . guid ,
message: `Here's a ${ category } recommendation: ${ randomUrl } ` ,
richLink: true ,
});
break ;
}
}
});
When a rich link is sent, the message includes special metadata:
const message = await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: "https://photon.codes/" ,
richLink: true ,
});
console . log ( message . balloonBundleId ); // Identifies rich link type
console . log ( message . payloadData ); // Contains link metadata
Best Practices
Validate URLs before sending
Ensure the URL is valid and accessible: function isValidUrl ( string : string ) {
try {
new URL ( string );
return true ;
} catch ( _ ) {
return false ;
}
}
if ( isValidUrl ( url )) {
await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: url ,
richLink: true ,
});
}
Handle preview generation failures
Not all URLs will generate previews. Wrap in try-catch: try {
await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: url ,
richLink: true ,
});
} catch ( error ) {
console . error ( "Failed to send rich link:" , error );
// Optionally send as plain text
await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: url ,
});
}
Add delays between sending multiple rich links: for ( const url of urls ) {
await sdk . messages . sendMessage ({ ... });
await new Promise ( r => setTimeout ( r , 2000 ));
}
Rich link previews work best with URLs that have proper Open Graph or Twitter Card metadata.
Supported URL Types
Rich link previews work with:
Websites with Open Graph metadata
YouTube videos
Twitter posts
Articles with proper meta tags
GitHub repositories
App Store apps
Most modern web content
Troubleshooting
If rich links aren’t working:
Check URL validity - Ensure the URL is accessible and returns a 200 status
Verify metadata - The website should have Open Graph or similar meta tags
Test in native iMessage - If it doesn’t work there, it won’t work via SDK
Check network - Ensure the server can access the URL
Plain URLs vs Rich Links
// Sends as plain text link
await sdk . messages . sendMessage ({
chatGuid: CHAT_GUID ,
message: "https://photon.codes/" ,
});
Next Steps
Message Effects Add visual effects to messages
Polls Create interactive polls