> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream discovery events

> Get progressive discovery revisions pushed over Server-Sent Events

`GET /api/v2/buyer/discovery/{discoveryId}/events`

Opens a [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) stream that pushes each newly landed seller group from a [progressive discovery](/v2/buyer/discovery/tasks/discover-products#progressive-delivery) as it lands, instead of you polling [Browse products](/v2/buyer/discovery/tasks/browse-products) with `sinceRevision`. Same continuation contract, delivered as a stream: the server watches the discovery snapshot server-side and emits an event whenever the `revision` advances.

This is a REST/browser-friendly alternative for callers that want push instead of poll — MCP clients get the equivalent behavior for free via progress notifications during the tool call, then continue with `browse_discovery?sinceRevision`.

## Request

The discovery session must already exist — start one with [Discover products](/v2/buyer/discovery/tasks/discover-products) using `progressive: true` first.

```bash curl theme={null}
curl -N "https://api.interchange.io/api/v2/buyer/discovery/disc_01HZX3YQ7K9R6V3M2P1E0F8B2T/events" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Accept: text/event-stream"
```

```javascript EventSource theme={null}
const events = new EventSource(
  `https://api.interchange.io/api/v2/buyer/discovery/${discoveryId}/events`,
  { withCredentials: true }, // send your session cookie or use a fetch-based SSE client for Bearer auth
)

events.addEventListener('revision', (e) => {
  const { revision, resultsComplete, pendingAgents, productGroups } = JSON.parse(e.data)
  render(productGroups) // replace-by-groupId, same as a sinceRevision poll
})

events.addEventListener('complete', (e) => {
  const { incompleteAgents } = JSON.parse(e.data)
  events.close()
})
```

## Parameters

| Field         | Type   | Required | Notes                          |
| ------------- | ------ | -------- | ------------------------------ |
| `discoveryId` | string | Yes      | Path param — session to stream |

## Response

`Content-Type: text/event-stream`. The stream emits these named events, each a `data:` line of JSON:

| Event      | When                                                | Payload                                                                                                                                                                                                                                        |
| ---------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ready`    | Immediately on connect                              | `{ discoveryId }`                                                                                                                                                                                                                              |
| `revision` | Whenever the snapshot's revision advances           | `{ discoveryId, revision, resultsComplete, productGroups, pendingAgents }` — `productGroups` contains only groups that landed since the previous revision you saw, using the same **replace-by-`groupId`** semantics as a `sinceRevision` poll |
| `complete` | Once, when `resultsComplete` becomes `true`         | `{ discoveryId, revision, incompleteAgents }` — the stream ends immediately after                                                                                                                                                              |
| `error`    | On an internal failure or the stream's max duration | `{ code, message }` — the stream ends immediately after                                                                                                                                                                                        |
| `: ka`     | Every \~15 seconds while otherwise idle             | Comment line (no `event:`/`data:`), ignored by SSE parsers — keeps proxies from closing an idle connection                                                                                                                                     |

Worked example — a discovery with two sellers still outstanding, then completion:

```
event: ready
data: {"discoveryId":"disc_01HZX3YQ7K9R6V3M2P1E0F8B2T"}

event: revision
data: {"discoveryId":"disc_01HZX3YQ7K9R6V3M2P1E0F8B2T","revision":1,"resultsComplete":false,"pendingAgents":[{"agentId":"agent_slow_media","agentName":"Slow Media Storefront"},{"agentId":"agent_ctv_net","agentName":"CTV Network"}]}

: ka

event: revision
data: {"discoveryId":"disc_01HZX3YQ7K9R6V3M2P1E0F8B2T","revision":2,"resultsComplete":false,"productGroups":[{"groupId":"ctx_disc-group-2","groupName":"CTV Network","products":["..."]}],"pendingAgents":[{"agentId":"agent_slow_media","agentName":"Slow Media Storefront"}]}

event: revision
data: {"discoveryId":"disc_01HZX3YQ7K9R6V3M2P1E0F8B2T","revision":3,"resultsComplete":true,"productGroups":[{"groupId":"ctx_disc-group-0","groupName":"Slow Media Storefront","products":["..."]}]}

event: complete
data: {"discoveryId":"disc_01HZX3YQ7K9R6V3M2P1E0F8B2T","revision":3}
```

The connection closes automatically after the `complete` event, on a hard 5-minute cap, or when you close it client-side. `pendingAgents` (still answering) is distinct from `incompleteAgents` (timed out or answered partially at the final revision) — same distinction as the poll contract.

<Note>
  The stream is served from whichever pod handles your connection, which is not necessarily the pod running the discovery fan-out. It polls the shared discovery snapshot server-side on your behalf (about once a second) — you don't need to poll anything yourself once the stream is open.
</Note>

## Errors

Errors before the stream opens use the standard JSON error envelope, since no `event:` frame has been written yet:

* `401 UNAUTHORIZED` — missing or invalid auth.
* `404 NOT_FOUND` — `discoveryId` does not exist or is not visible to the authenticated account.
* `400 VALIDATION_ERROR` — the discovery session exists but is not active.

See [Errors](/v2/reference/errors) for the full error contract. Once the stream has opened, failures surface as an `error` event instead — the connection cannot switch back to a JSON response body.

## Related

<CardGroup cols={2}>
  <Card title="Discover products" href="/v2/buyer/discovery/tasks/discover-products" icon="magnifying-glass">
    Run discovery and open a progressive session
  </Card>

  <Card title="Browse products" href="/v2/buyer/discovery/tasks/browse-products" icon="layer-group">
    Poll for the same revisions instead of streaming
  </Card>

  <Card title="Discovery overview" href="/v2/guides/discovery" icon="rocket">
    Sessions, refinement, proposals, and storefronts
  </Card>

  <Card title="Webhooks" href="/v2/buyer/webhooks" icon="webhook">
    Get the same revisions pushed to your own endpoint instead
  </Card>
</CardGroup>
