> 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/concepts/two-paths.md).

# Two paths, one type

The SDK reads events two ways. Both return the same `DecodedEvent`, so you write one handler and choose the source as a deployment decision.

## The indexer path

`client.events`, `client.eventStream`, and `client.event` read from a running Pulsar indexer.

* It has decoded and stored everything, so it reaches back through all of history, past the roughly seven days Soroban RPC keeps.
* It pages backward cheaply.
* It needs infrastructure: an indexer process and its database.

## The RPC path

`fetchLiveEvents` and `liveEventStream` read straight from Stellar RPC.

* It sees an event the moment its ledger closes.
* It needs nothing beyond a public RPC endpoint.
* It reaches back only as far as that node's retention window, roughly a week. A `startLedger` older than the window is rejected by RPC, not by the SDK, because the window is per node and moves between calls.

## What is the same

Both paths produce `DecodedEvent`. The decoding rules are identical, so an event read live and the same event read later from the indexer decode to the same shape. A consumer writes one handler either way.

## What differs

|              | Indexer path                        | RPC path                             |
| ------------ | ----------------------------------- | ------------------------------------ |
| Methods      | `events`, `eventStream`, `event`    | `fetchLiveEvents`, `liveEventStream` |
| Reaches back | all of history                      | about a week                         |
| Needs        | a running indexer                   | a public RPC URL                     |
| Page cursor  | `nextCursor`, null on the last page | `cursor`, never null                 |
| Ends?        | yes, when `nextCursor` is null      | no, the tail has no end              |
| Event id     | a string of digits                  | `rpc:` followed by the RPC id        |

The id prefix is worth knowing. An indexer event id is a plain string of digits. An RPC event id carries the prefix `rpc:` so the two sources never collide in a store that holds both.

## Why the indexer path ends and the RPC path does not

The indexer owns a finite, stored history. When you page to the end of it, `nextCursor` is null. Null and an absent cursor field both mean the same thing: there is no next page. So you page until you see null and never check two falsy values.

```ts
let cursor: string | undefined;
do {
  const page = await client.events(contractId, cursor === undefined ? undefined : { cursor });
  for (const event of page.items) handle(event);
  cursor = page.nextCursor ?? undefined;
} while (cursor !== undefined);
```

The RPC path has no end to reach. The live network keeps closing ledgers, so RPC returns a cursor on every response, including empty ones. There is no exhaustion signal to report, and `LiveEventsPage.cursor` is never null. Reaching the tip means empty pages, not a final one. `liveEventStream` waits and asks again.

```ts
for await (const event of liveEventStream(client.config, { startLedger: latest })) {
  handle(event);
  if (done) break; // breaking is how you stop; nothing is prefetched
}
```

Deciding when to stop is yours, because only the network knows whether more is coming. See [Error handling](/pulsar-stellar-sdk/concepts/error-handling.md) for what a failure part way through a stream leaves you with.

## eventIndex is a ledger ordinal

On both paths, `eventIndex` is the event's position within its ledger, not within its transaction. Soroban RPC offers no per-transaction index: its `transactionIndex` and `operationIndex` repeat across events, and only the event id's second component increments across a ledger. Events from one transaction stay contiguous, so ordering within a transaction still works. Do not read `eventIndex` as "the Nth event in this transaction."


---

# 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/concepts/two-paths.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.
