Reading Messages
Last message
The most common fetch — get the most recent message in a thread:
const message = await thread.loadLastMessage();
if (message) {
const content = await message.loadContent();
console.log(content.text);
}
Returns null if the thread has no messages yet.
Loading a range
loadMessages walks the sequence number range and returns an array of loaded MessageClient instances.
// Last 20 messages (newest first by default)
const messages = await thread.loadMessages({ limit: 20 });
// Paginate backward from a given seq
const older = await thread.loadMessages({
limit: 20,
fromSeq: 40,
direction: "backward",
});
| Field | Type | Default | Description |
|---|---|---|---|
limit | number | total message count | Max messages to return |
fromSeq | number | lastMsgSeq | Starting sequence number |
direction | "backward" | "forward" | "backward" | Walk direction |
All fetches in the range are parallelised internally.
Single message by sequence
const message = await thread.loadMessage(3);
Or build a handle without fetching and load it later:
const handle = thread.getMessage(3);
await handle.load();
Message content
Call loadContent() to resolve the message body. Inline text stays local; URL/Irys/IPFS/Arweave content is fetched and returned as bytes with optional text.
const result = await message.loadContent();
if (result.kind === "inline") {
console.log(result.text); // Text message stored on-chain
} else {
console.log(result.url); // Gateway/source URL
console.log(result.contentType); // HTTP Content-Type when available
console.log(result.bytes); // Raw bytes
console.log(result.text); // Only set for textual MIME types
}
MessageType | Storage | Behaviour |
|---|---|---|
Text (0) | Inline, on-chain | Reads the buffer directly — no network call. |
Url (1) | External URL | Fetches the URL over HTTP. |
Ipfs (2) | IPFS | Fetches from the IPFS gateway URL in the message. |
Irys (3) | Irys / Arweave | Fetches from the Irys URL. |
Arweave (4) | Arweave | Same as Irys. |
Binary resources are not force-decoded into strings. For example, an unencrypted uploaded PNG returns bytes and contentType: "image/png", while text stays undefined.
Parsed content
Use loadParsedContent() when you want the normal Packet reader flow:
const parsed = await message.loadParsedContent({ decrypt: true });
console.log(parsed.encrypted);
console.log(parsed.subject);
console.log(parsed.message);
console.log(parsed.mediaKind); // "text" | "binary"
It loads inline/external content, decrypts textual encrypted JSON when requested, parses the Packet envelope, and returns display text plus raw bytes/text metadata.
For binary Packet envelope parts, parsed.message contains a preview such as [binary image/png, ~24 KB base64], and parsed.parts keeps the structured PacketContent values for download/resource rendering.
MessageClient reference
Static methods
MessageClient.Handle({ client, threadId, msgSeq })
Creates a handle without fetching. Load later with .load().
const handle = MessageClient.Handle({ client, threadId: 42, msgSeq: 3 });
await handle.load();
Returns MessageClient
Instance methods
| Method | Description |
|---|---|
load(force?) | Fetch from chain. Skips if already loaded unless force is true. |
loadRetrying(retries?, delay?) | Fetch with retries — useful right after sending when the tx may not yet be visible. |
refresh() | Force-reload and clear cached content. |
loadContent(force?) | Resolve content (inline read or external fetch). |
loadParsedContent({ decrypt?, force? }) | Load, optionally decrypt, parse Packet envelope, and classify text/binary media. |
loadRetrying parameters (positional):
| Parameter | Type | Default | Description |
|---|---|---|---|
retries | number | 3 | Max retry attempts |
delay | number | 250 | Delay between retries in milliseconds |
Getters
| Getter | Type | Description |
|---|---|---|
Loaded | boolean | Whether message data has been fetched |
Message | Message | Raw on-chain data (throws if not loaded) |
Content | MessageContentLoadResult | Resolved content (throws if loadContent not called) |
Types
interface Message {
threadId: number;
msgSeq: number;
senderSide: MessageSenderSide; // 0 = from, 1 = to
timestamp: number;
payment: Payment | null;
messageType: MessageType;
content: Buffer; // raw bytes; URL string for non-Text types
}
type MessageContentLoadResult = {
kind: "inline" | "url";
bytes: Uint8Array;
text?: string;
contentType?: string;
url?: string;
messageContentKind: "text" | "url" | "irys" | "ipfs" | "arweave";
};
enum MessageSenderSide {
From = 0, // the wallet that opened the thread
To = 1, // the wallet that received it
}
Message sequence numbers
Messages are numbered starting from 1. The thread tracks lastMsgSeq (the highest sent) and totalMsgs. For pagination, walk backward from lastMsgSeq in batches until you reach seq 1.