# Resolve a chat link

`POST /chat.link.resolve`

## Description

Parse a Roam chat deep link (e.g. `https://ro.am/r/#/d/...`) and return the
referenced message.

When the caller has access to the referenced chat, the full message is
returned and `readable` is `true`. The `message` object is the same
shape as a `chat.history`/`chat.search` message — same fields, same
mention rendering. When the caller lacks access, the response still
includes the message key (`chatId`, `timestamp`, and `threadTimestamp`
if applicable) with `readable: false` and no message content — suitable
for rendering a reference without leaking content.

Use [`/chat.link.create`](/docs/api/chat-link-create) for the reverse
operation — minting a shareable Roam link from a message the caller can
already read.

**Access:** Organization and Personal.

**Required scope:** `chat:history`

---

**OpenAPI Spec:** [chat-v1.json](https://developer.ro.am/chat-v1.json)

## Authentication

```
Authorization: Bearer YOUR_API_KEY
```

## Request Body

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `link` | string | Yes | A Roam chat deep link URL that contains a message reference. |

### Example Request

```json
{
  "link": "https://ro.am/r/#/d/abc123xyz/c/757dfe66-37b4-4772-baa5-8c86ec68c176?ts=1765602474760032"
}
```

## Responses

### 200 - Link resolved. When `readable` is false the caller lacks access to the chat; only the message key is returned.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `chatId` | string | Yes | ID of the chat referenced by the link. |
| `timestamp` | integer | Yes | Timestamp of the referenced message (microseconds). |
| `threadTimestamp` | integer | No | Thread timestamp if the referenced message is in a thread. |
| `readable` | boolean | Yes | `true` if the caller has access to the chat and `message` is populated. `false` if the caller lacks access; no message content is returned.  |
| `message` | ChatMessage | No |  |

**ChatMessage**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | "message" | No | Message type identifier |
| `userId` | string | Yes | Principal ID of the message sender. Resolve with `user.info`. |
| `userType` | "user" | "bot" | Yes | Principal type of `userId`; always equals `user.info.type` for the same credentials. Use it to prevent bot loops without another lookup. |
| `chatId` | string | No | ID of the chat the message belongs to |
| `timestamp` | integer | Yes | Message key as Unix microseconds |
| `threadTimestamp` | integer | No | Unix microseconds timestamp of the parent message (for thread replies) |
| `replyTimestamp` | integer | No | Timestamp of the message this one quotes — a DM or channel-thread quoted reply, set via the `replyTimestamp` request field on chat.post. Omitted otherwise. |
| `ephemeral` | boolean | No | Whether the message is ephemeral. Always omitted (false) in chat.history, chat.search, and chat.link.resolve responses — ephemeral messages (posted via chat.postEphemeral) are never persisted, so they |
| `text` | string | No | Text of the message, formatted as github-flavored markdown. Mention tokens use Slack's syntax: `<@ID>` is a principal (user or bot — resolve with `user.info`), `<!subteam^ID>` is a group or channel (r |
| `contentType` | "text" | "voice" | "block" | "poll" | Yes | Type of message content: `text` (markdown body, optionally with items), `voice` (voice note), `block` (rich block layout), or `poll`. |
| `items` | ChatItem[] | No | Items attached to this message |
| `poll` | object | No | Poll content, present when contentType is `poll`. |
| `voice` | object | No | Voice-note content, present when contentType is `voice`. |
| `blocks` | object[] | No | Rich block layout, present when contentType is `block`. |
| `color` | string | No | Accent color for a block message, present when contentType is `block`. Omitted otherwise. |
| `replyCount` | integer | No | Number of replies in this message's thread. Omitted when zero. |
| `sender` | object | No | Per-message sender display override supplied at send time via the request's `sender` field. Present only when the stored message carries one. Additive: `userId` remains the authoring identity — render |
| `mentions` | string[] | No | Flat, order-preserving, de-duplicated list of everything referenced by mention tokens in `text`: bare address UUIDs (from both `<@ID>` principal and `<!subteam^ID>` group tokens), plus the literal `al |

**ChatItem** (array item):

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | Yes | UUID identifying this item |
| `type` | "photo" | "blob" | Yes | Type of item:  - **photo**: Images with inline preview and thumbnail   - image/jpeg, image/png, image/gif, image/webp  - **blob**: Any other file type (download only, no preview)   - application/octet |
| `mime` | string | No | MIME type of the file (e.g., "application/octet-stream"). May be omitted for photo items where the type is inferred from the image format.  |
| `created` | string | Yes | Timestamp when the item was created |
| `name` | string | Yes | Name of the item (typically the filename). |
| `url` | string | Yes | URL for the uploaded item. |
| `thumbnail` | string | No | URL for a thumbnail of the uploaded item (photo type only). This may be equal to the item's main URL if it is suitable to use as a thumbnail.  |
| `size` | integer | No | Size of the item in bytes |
| `width` | integer | No | Width in pixels (images only) |
| `height` | integer | No | Height in pixels (images only) |

**poll**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `question` | string | No | The poll question. |
| `options` | object[] | No | The poll answer options. |
| `allowMultipleAnswers` | boolean | No | Whether voters can select multiple options. |
| `closesAt` | string | No | When the poll closes (RFC-3339). Omitted if no close time is set. |

**voice**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `audioUrl` | string | Yes | URL of the voice-note audio (m4a). |
| `duration` | integer | Yes | Duration of the voice note in milliseconds. |
| `transcript` | string | No | Text transcript of the voice note. Omitted if not yet available. |

**sender**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | No | Display name override for this message. |
| `imageUrl` | string | No | Avatar URL override for this message. |

### 400 - Bad request. Common causes:
- Missing `link`
- Link cannot be parsed
- Link does not contain a message reference


### 401 - Presented invalid authentication credentials.

### 404 - The referenced message does not exist.

### 405 - An unsupported method was requested.

### 500 - An internal error occurred.

---

*Machine-readable API documentation.*
*Full documentation: https://developer.ro.am/docs/api/chat-link-resolve*
