Skip to main content

Overview

The Sinker class maintains a single persistent SSE connection to GET /internal/events. All event types are multiplexed over this one connection and dispatched to the appropriate handlers via sinker.on().
sinker.on('tx_status_changed', (e) => { /* ... */ });
sinker.on('bundle_settled',    (e) => { /* ... */ });
sinker.on('slot_update',       (e) => { /* ... */ });
sinker.on('tx_enqueued',       (e) => { /* ... */ });
sinker.on('submit_requested',  (e) => { /* ... */ });
The stream reconnects automatically with exponential backoff if the sidecar restarts or the connection drops.

Event types

tx_status_changed

Fired whenever a transaction moves to a new commitment state. This is the primary event for tracking transaction progress.
sinker.on('tx_status_changed', (e) => {
  console.log(e.tx_id);          // "346e5b0e-..."
  console.log(e.state);          // "confirmed" | "finalized" | "failed" | ...
  console.log(e.bundle_id);      // "b9e8b294..." | null
  console.log(e.tx_signatures);  // ["3b9QQcUQ..."]
  console.log(e.failure);        // "fee_too_low" | null
});
tx_id
string
The transaction ID from /tx/enqueue.
state
TxState
New state: pending | submitted | landed | processed | confirmed | finalized | failed | expired
bundle_id
string | null
The bundle that produced this state transition.
tx_signatures
string[]
On-chain signatures, populated once the transaction lands.
failure
string | null
Failure class name when state is "failed".

bundle_settled

Fired when a bundle reaches a terminal state (finalized or failed). The AI agent listens to this event to decide whether to retry.
sinker.on('bundle_settled', (e) => {
  console.log(e.bundle_id);     // "b9e8b294..."
  console.log(e.stage);         // "finalized" | "failed"
  console.log(e.failure_type);  // "fee_too_low" | null
});
bundle_id
string
The settled bundle’s ID.
stage
string
Terminal stage: "finalized" or "failed".
failure_type
string | null
Failure class name when stage is "failed".

slot_update

Fired on every slot event received from the Yellowstone gRPC stream.
sinker.on('slot_update', (e) => {
  console.log(e.slot);    // 423602205
  console.log(e.leader);  // "GiYSnFRrXrm..." | null
});
slot
number
The new slot number.
leader
string | null
The validator identity pubkey scheduled for this slot, or null if not in the leader buffer.

tx_enqueued

Fired immediately when POST /tx/enqueue succeeds. The AI agent uses this as the primary trigger to start a decision cycle.
sinker.on('tx_enqueued', (e) => {
  console.log(e.tx_id);  // "346e5b0e-..."
  console.log(e.slot);   // 423602204
});

submit_requested

Fired when POST /internal/submit is called — either by the AI agent or by a manual call to sinker.submit(). Useful for observability dashboards.
sinker.on('submit_requested', (e) => {
  console.log(e.tip_lamports);  // 75000
  console.log(e.reason);        // "p75 — 1 prior fee_too_low"
  console.log(e.leader);        // "GiYSnFRrXrm..."
});

Per-transaction streams

For a filtered stream scoped to a single transaction, handle.waitFor() uses the GET /tx/events/:tx_id endpoint internally. This stream:
  • Sends only tx_status_changed events for the requested tx_id
  • Closes automatically after the first terminal state (finalized, failed, expired)
  • Sends the current state immediately if the tx is already terminal when the client connects
You rarely need to use this endpoint directly — handle.waitFor() wraps it cleanly.

SSE reconnection

The SDK reconnects automatically using exponential backoff:
AttemptWait before retry
1500ms
21,000ms
32,000ms
44,000ms
doubles each time, cap 30s
Events emitted during a reconnect gap may be missed. The 30-second agent watchdog exists specifically to handle this case — it re-checks queue state and re-submits if needed.