Skip to main content

Events

Packet exposes live event helpers for UI refreshes and agents that are already running.

These APIs are live subscriptions, not historical indexers. They do not replay missed messages. Use thread/inbox/activity loading APIs when you need past messages.


Message events

client.messageEvents listens to MessageSent Anchor emit_cpi! events through client.cpiEvents.

const sub = client.messageEvents.listenIncoming({
onMessage: async (message, event) => {
console.log("new message", event.threadId, event.msgSeq);

// The event gives you a MessageClient handle.
// Load the account if you need raw message data.
await message.loadRetrying(5, 500);
},
onError: console.error,
});

await sub.stop();

This listener subscribes to program logs, fetches the transaction, decodes CPI-emitted Anchor events, and then applies SDK-level filters.

Methods

MethodDescription
client.messageEvents.listen(params)Listen to all live message events with optional filters.
client.messageEvents.listenIncoming(params)Shorthand for messages where receiver is client.walletPublicKey.
client.messageEvents.listenOutgoing(params)Shorthand for messages where sender is client.walletPublicKey.
client.messageEvents.listenThread(threadId, params)Listen to live events in one thread.

ListenMessagesParams

FieldTypeDescription
onMessage(message: MessageClient, event: PacketMessageSentEvent) => void | Promise<void>Called for each matching live event.
onError(error: unknown) => voidCalled when decoding/loading/callback processing throws.
threadIdnumberFilter to a specific thread.
senderPublicKeyFilter by sender.
receiverPublicKeyFilter by receiver.
incomingForPublicKeyReceiver must equal this key.
outgoingFromPublicKeySender must equal this key.
eventName"messageSent" | stringEvent name compatibility override.
commitmentFinalityWebSocket/getTransaction commitment.
maxRetriesnumberRetry count while waiting for the transaction to become fetchable.

PacketMessageSentEvent

type PacketMessageSentEvent = {
threadId: number;
msgSeq: number;
sender: PublicKey;
receiver: PublicKey;
slot: number;
signature?: string;
};

Inbox account events

InboxClient.listenEvents listens to live account changes for one inbox. It is cheaper than listening to every message event when you only need to know that one inbox changed.

const inbox = await client.inbox(0);

const sub = inbox.listenEvents({
clearPages: true,
onChange: async (inboxClient, event) => {
console.log("inbox changed at slot", event.slot);
console.log("new len", inboxClient.Inbox.len.toString());
},
onError: console.error,
});

await sub.stop();

ListenInboxEventsParams

FieldTypeDescription
commitmentFinalityAccount-change subscription commitment. Defaults to confirmed.
clearPagesbooleanClear cached inbox body/archive pages when the inbox account changes.
filter(event: InboxChangedEvent) => boolean | Promise<boolean>Suppress changes by returning false.
onChange(client: InboxClient, event: InboxChangedEvent) => void | Promise<void>Called for each matching account change.
onError(error: unknown) => voidCalled if decoding/filter/callback processing throws.

InboxChangedEvent

type InboxChangedEvent = {
address: PublicKey;
previous: Inbox;
inbox: Inbox;
slot: number;
accountInfo: AccountInfo<Buffer>;
};

Low-level CPI event client

client.cpiEvents is available for advanced callers who need to decode other Anchor CPI events.

const sub = client.cpiEvents.listen({
eventName: ["messageSent", "MessageSent"],
parse: (raw, ctx) => ({ raw, signature: ctx.signature }),
filter: (event) => Boolean(event.raw),
onEvent: async (event) => {
console.log(event.signature);
},
});

Most apps should use client.messageEvents or inbox.listenEvents instead.


Subscription lifecycle

All event methods return:

type PacketEventSubscription = {
id: number;
stop: () => Promise<void>;
};

Call stop() when a component unmounts, an agent loop exits, or a CLI process is shutting down.