Skip to main content

Unfurling links

Roam apps can receive matching links from new messages and attach rich preview cards without Roam fetching the destination. This is useful for incident, ticket, document, and deployment links whose best summary is available only to your app.

1. Register domains

In Roam Administration → Developer → API Client → Webhooks, add up to five Link Unfurling Domains. Exact hosts match only themselves. A leading wildcard matches exactly one additional label:

RegistrationMatchesDoes not match
status.example.comstatus.example.comwww.status.example.com
*.example.comstatus.example.comexample.com, a.status.example.com

Roam canonicalizes entries to lowercase ASCII hostnames. Schemes, paths, ports, IP addresses, localhost, public suffixes, and misplaced wildcards are rejected. Domain changes may take up to one minute to propagate in production.

Works with API Key and OAuth clients. Personal Access Token clients cannot register unfurl domains (or call chat.unfurl). OAuth installations inherit the domains configured on their parent OAuth app.

Request links:read, configure a webhook URL, and subscribe to chat.link.shared. Each delivery uses the common webhook envelope. Its data.links list contains only URLs matching that installation's registrations, uniquely and in message order.

The event fires for newly created text and markdown messages only. It does not fire for edits or messages sent by that same app installation, preventing an unfurl response loop.

3. Attach previews

Request links:write and call POST /chat.unfurl with the event's chatId, messageTimestamp, and one or more exact URL keys:

{
"chatId": "8f3b9c2e-1a4d-4e7b-9c0a-2b6d1f5e3a7c",
"messageTimestamp": 1748906400000000,
"unfurls": {
"https://status.example.com/incidents/123": {
"title": "Incident 123",
"description": "Investigating elevated errors",
"siteName": "PagerDuty"
}
}
}

Every URL is checked against the message's current text and the app's current domain registrations. If any URL fails, the whole request is rejected with cannot_unfurl_url and no cards change. App cards replace Roam-generated cards for the same exact URL, preserve unrelated previews, and appear in message-link order. The latest valid app update wins.

favicon and image.url must be https display URLs. Roam's server does not fetch them; clients render them with the existing link-preview card.

Successful unfurls currently persist as an ordinary message edit (version bump). Clients may show an Edited label on the original message, and subscribers with chat:history receive a chat.message edit event. Suppressing the Edited badge for preview-only writes is planned as a follow-up.