# How to Access a Forwarded Message

When a user forwards an existing message into a conversation, the incoming webhook contains a `share_link_id` field. Use that ID to fetch the original message content — transcript, audio, and attachments — via the share link endpoint.

## Detecting a Forwarded Message

A forwarded message arrives as a normal `message.posted.to.channel` webhook event. Check for a non-null `share_link_id` in the payload to identify it:

```json
{
  "event": "message.posted.to.channel",
  "data": {
    "id": "new-message-uuid",
    "conversation_id": "channel-uuid",
    "share_link_id": "share-link-uuid",
    "transcript_txt": "Check this out",
    "creator_id": "user-who-forwarded-uuid"
  }
}
```

`share_link_id` being non-null is the only signal you need — `transcript_txt` may also be present if the sender added accompanying text, but the original forwarded content lives behind the share link.

## Fetch the Original Message Content

```
GET /v6/message-sharelinks/{share_link_id}
Authorization: Bearer <your-pat>
```

**Response — key fields:**

| Field | Description |
| --- | --- |
| `share_type` | `"forward"` for forwarded messages, `"link"` for public share links |
| `created_by` | User who performed the forward |
| `end_access_at` | Expiration timestamp (null if no expiry) |
| `revoked_at` | Set if access has been revoked |
| `has_channel_access` | Whether the authenticated user has access to the original message's conversation |
| `shared_message` | The original message, in the same MessageV6 shape the `/v6/messages` endpoints return (see below) |

**`shared_message` fields:**

| Field | Description |
| --- | --- |
| `id` | ID of the original message |
| `creator_id` | Who originally sent the message |
| `conversation_id` | The conversation the original message belongs to |
| `workspace_id` | The workspace the original message belongs to |
| `content.transcript` | The transcript of the original message as plain text |
| `content.ai_summary` | AI summary of the original message, when one exists |
| `content.time_codes` | Word-level time codes, when available |
| `content.url` | Playback URL of the original audio |
| `content.duration_ms` | Duration of the original voice message |
| `attachments` | Attachments from the original message. For `type: "file"`, `url` already points at `GET /message-sharelinks/{share_link_id}/attachments/signedurl/{id}`, which returns the download URL |
| `created_at` | When the original message was sent |

> **To get the original message ID**, read `shared_message.id` directly.

## Access Attachments and Audio

Play the original audio from `shared_message.content.url`.

File attachments are stored privately and are **not** directly downloadable. The `url` of each `type: "file"` entry in `shared_message.attachments` is the share-link-scoped signed URL endpoint, which validates share-link access rather than requiring conversation membership:

```
GET /message-sharelinks/{share_link_id}/attachments/signedurl/{attachment_id}
Authorization: Bearer <your-pat>
```

The response is a signed URL you can use to download the file. Do not use `GET /v5/attachments/download/signedurl/{attachment_id}` here — that endpoint checks access to the original message's conversation and will 403 when `has_channel_access` is false.

## Full Example — Webhook Handler

```javascript
async function handleWebhook(webhook) {
  const { share_link_id, conversation_id, id } = webhook.data;

  if (!share_link_id) return; // not a forwarded message

  const res = await fetch(
    `https://api.carbonvoice.app/v6/message-sharelinks/${share_link_id}`,
    {
      headers: {
        Authorization: `Bearer ${process.env.CV_PAT}`,
      },
    },
  );
  const shareLink = await res.json();

  if (shareLink.revoked_at) return; // access revoked

  const { shared_message } = shareLink;
  const originalMessageId = shared_message.id;
  const transcript = shared_message.content?.transcript ?? '';

  // reply with a summary or acknowledgement
  await fetch('https://api.carbonvoice.app/v6/messages/text', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CV_PAT}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      conversation_id,
      transcript: `Got the forwarded message: "${transcript}"`,
      reply_to_message_id: id,
    }),
  });
}
```

## Related

- [How to Handle Incoming Webhook Payloads](./how-to-handle-incoming-webhook-payloads.md)
- [How to Send an Existing Message](./how-to-send-an-existing-message.md)
- [How to Download a File Attachment](./how-to-download-a-file-attachment.md)
