> For the complete documentation index, see [llms.txt](https://emeditweb.gitbook.io/pulsar-stellar-sdk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://emeditweb.gitbook.io/pulsar-stellar-sdk/sdk-reference/rpc.md).

# Live RPC

The RPC path reads events straight from Stellar RPC, bypassing the indexer. It needs only a public RPC URL and sees an event the moment its ledger closes. It reaches back only as far as the node's retention window, roughly a week.

These are free functions, not client methods. They take a [`ResolvedPulsarConfig`](/pulsar-stellar-sdk/sdk-reference/schemas-and-types.md#pulsarconfigschema), which you get from [`client.config`](/pulsar-stellar-sdk/sdk-reference/client.md#clientconfig). What they return is a [`DecodedEvent`](/pulsar-stellar-sdk/sdk-reference/schemas-and-types.md#decodedevent), the same type the indexer path produces.

## `fetchLiveEvents(config, query)`

```ts
fetchLiveEvents(config: ResolvedPulsarConfig, query: LiveEventQuery): Promise<LiveEventsPage>
```

Reads one page of events from RPC.

* **`config`**: must carry an `rpcUrl`. Without one, this throws `PulsarValidationError` before any request.
* **`query`**: a [`LiveEventQuery`](#liveeventquery), exactly one of `startLedger` or `cursor`.
* **Returns** [`LiveEventsPage`](#liveeventspage): `{ events, cursor, latestLedger }`. `cursor` is never `null`.
* **Throws** `PulsarValidationError` if the query sets both modes or neither, if the config has no `rpcUrl`, or if an event does not decode into a valid `DecodedEvent`.
* **Throws** [`PulsarNetworkError`](/pulsar-stellar-sdk/sdk-reference/errors.md#pulsarnetworkerror) if RPC is unreachable or answers with an error, with the underlying failure as `cause`.

A `startLedger` older than the retention window is rejected by RPC, not by the SDK, because the window is per node and moves between calls.

```ts
import { fetchLiveEvents } from '@pulsar-stellar/sdk';

const page = await fetchLiveEvents(client.config, {
  startLedger: 1_000_000,
  filter: { contractIds: ['CDNWTVUDKCCGW7GOC6SBLUFXXUCD2YDHWRDUSXZ6CYBQKQWLCUYYWI5L'] },
});

for (const event of page.events) console.log(event.ledger, event.name);
console.log('resume from', page.cursor);
```

## `liveEventStream(config, query, options?)`

```ts
liveEventStream(
  config: ResolvedPulsarConfig,
  query: LiveEventQuery,
  options?: LiveEventStreamOptions,
): AsyncIterable<DecodedEvent>
```

Follows events as their ledgers close, fetching pages as needed.

**The loop does not end on its own.** RPC reports no exhaustion, so reaching the tip means empty pages, not a final one, and the stream waits `pollIntervalMs` and asks again. Breaking out of the `for await` is how you stop; nothing is prefetched, so breaking costs no extra request. Each iteration starts a fresh traversal from the original query.

* **`options.pollIntervalMs`**: gap after an empty page before asking again. Defaults to [`DEFAULT_POLL_INTERVAL_MS`](#default_poll_interval_ms).
* **Throws**, out of the loop, `PulsarValidationError` or `PulsarNetworkError` on whichever page fails. Events already yielded stay valid.

```ts
import { liveEventStream } from '@pulsar-stellar/sdk';

for await (const event of liveEventStream(client.config, { startLedger: 1_000_000 })) {
  if (event.name === 'deposit') break; // decide when you are done
}
```

## `LiveEventQuerySchema` and `LiveEventQuery`

```ts
type LiveEventQuery =
  | { startLedger: number; cursor?: never; limit?: number; filter?: LiveEventFilter }
  | { cursor: string; startLedger?: never; limit?: number; filter?: LiveEventFilter };
```

Where to start, by ledger or by cursor, never both. This mirrors RPC's own `getEvents`, which takes one or the other and rejects both together. The rule is enforced twice, per ADR-025: at compile time by the `never` members above, and at runtime by `LiveEventQuerySchema`, because a JavaScript caller reaches the function with no type checking.

Build each continuation fresh. Spreading the previous query carries `startLedger` forward and is a compile error:

```ts
let query: LiveEventQuery = { startLedger: 4_378_751 };
const page = await fetchLiveEvents(client.config, query);
query = { cursor: page.cursor };            // correct
// query = { ...query, cursor: page.cursor }; // compile error, on purpose
```

`limit` is optional and capped at [`EVENT_QUERY_MAX_LIMIT`](/pulsar-stellar-sdk/sdk-reference/schemas-and-types.md#constants) (`500`). `filter` is a [`LiveEventFilter`](#liveeventfilterschema-and-liveeventfilter).

## `LiveEventFilterSchema` and `LiveEventFilter`

```ts
type LiveEventFilter = {
  contractIds?: string[]; // at least one when present
  type?: 'contract' | 'system';
};
```

Which contracts and which event type to ask RPC for. Omit `contractIds` to take every contract's events. `type` has only `'contract'` and `'system'`: there is no `'diagnostic'` member, despite an example in the upstream SDK's own JSDoc showing one, per ADR-013. When omitted, `fetchLiveEvents` asks for `'contract'`.

## `LiveEventsPage`

```ts
interface LiveEventsPage {
  readonly events: DecodedEvent[];
  readonly cursor: string;       // never null
  readonly latestLedger: number;
}
```

One page of live events, oldest first, possibly empty. `cursor` is always present and never `null`: a page with events returns the last one's id, an empty page returns a positional marker so paging continues from where the scan stopped. `latestLedger` is the newest ledger RPC had closed when it answered.

## `LiveEventStreamOptions`

```ts
interface LiveEventStreamOptions {
  readonly pollIntervalMs?: number;
}
```

Milliseconds to wait after an empty page before asking again.

## `RPC_ID_PREFIX`

```ts
const RPC_ID_PREFIX = 'rpc:';
```

The prefix every RPC-sourced event id carries, per ADR-024, so RPC ids and indexer ids never collide in a store holding both. An RPC event's `id` is `rpc:` followed by the RPC id.

## `DEFAULT_POLL_INTERVAL_MS`

```ts
const DEFAULT_POLL_INTERVAL_MS = 2000;
```

The default gap between polls in `liveEventStream`, roughly a third of a ledger close time.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://emeditweb.gitbook.io/pulsar-stellar-sdk/sdk-reference/rpc.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
