# Synorb agent guide: Stream and source discovery

This page is part of the Synorb agent guide at https://synorb.com/agent-guide.md.
How to find Streams with `media_format`, `surface`, `home_domain`, and `source_class`, and how content BY an entity differs from content ABOUT an entity.

## Stream & source discovery

**`media_format` is the answer for "find podcast content"** and any other
format-scoped request. Podcasts are a media format, not a curated list of
organizations. Compose sector + format; do not reach for a curated Surface.
Pick `home_domains` + `media_format=audio`. If the request is for *all*
podcasts, select every domain + `audio`. A sector request (for example "sports
podcasts") is the `home_domain` of that sector + `audio`.

Worked example, business podcasts:

```bash
curl -sS -H "Authorization: Bearer $SYNORB_KEY" \
  'https://api.synorb.com/streams?media_format=audio&home_domain=economics-business-work'
```

Valid `media_format` values: `audio`, `video`, `social`, `text`, `data`,
`regulatory`. The enum is machine-readable in `openapi.json` on `GET /streams`,
`POST /streams/search`, and the `media_format` parameter/field of
`POST /manifests/query`. An unrecognized value returns `422` and names the
valid list. This includes the natural wrong guess `media_format=podcast`.

This happens on every interface that accepts the value (REST and MCP alike).
It never silently matches zero Streams or Manifests. `home_domains`/`home_domain`
are equally strict. An unrecognized domain also returns `422` and names the 12
canonical ids. It does not silently match zero. This holds for `GET /streams`,
`POST /streams/search`, `POST /manifests/query`, and `POST /manifests/plan`.

`GET /streams?surface=<id>` is a curated, zero-usage scope filter for a named
vertical (central banks, public companies, and similar). `surface=podcasts`
still works:

```bash
curl -sS -H "Authorization: Bearer $SYNORB_KEY" \
  'https://api.synorb.com/streams?surface=podcasts&home_domain=economics-business-work'
```

But it is no longer the recommended path for "find podcasts". It curates whole
organizations that happen to carry a podcast source channel (the Federal
Reserve, HHS, the UN), together with actual podcast shows. For a
podcast-discovery query, that set is wider and sometimes off-target. The
`surface=podcasts` set has 212 Streams, and the `media_format=audio` set has
188. The two sets overlap on 185 Streams but are different sets.

Prefer `media_format=audio` (+ `home_domains`) for any request that asks "find
me podcasts." Synorb accepts legacy hyphenated/family Surface ids and
normalizes them to the current id. The full canonical id list is
machine-readable as the `enum` on this parameter in `openapi.json`. An invalid
value returns `422` with every valid id under `valid_surfaces`.

It is an open founder decision whether `surface=podcasts` stays a permanent
convenience alias, or Synorb formally deprecates it in favor of
`media_format=audio`. This guide does not resolve it.

`GET /streams` has **no** `search=` filter. If you pass one, it returns
`400 unsupported_filter_param`. It does not silently return the full unfiltered
catalog (5,000+ Streams). Never infer that an unrecognized query param narrowed
the result set.

Use the separate zero-usage `POST /streams/search` ranked search for open-ended
free-text discovery. Examples are the name of a source or "business podcasts".
Use it when you do not already know a `media_format`, a `surface` id, or exact
`stream_ids`.

**Tag scoping compared with media_format/surface scoping.**
`GET /ontology/tags?search=` resolves named entities (people, organizations,
places). It also resolves a deliberately narrow, curated set of
macro-economic/financial `topic` concepts (`inflation`, `ai_regulation`,
`monetary_policy`, and similar). It does not carry media-format concepts.
`search=podcast` does not return a "podcast" topic Tag. That is intentional
scope, not a gap.

When you want a media format or vertical, and not a named entity or curated
topic, use `media_format=` on `GET /streams`. For a curated vertical, use
`surface=` on `GET /streams`. Do not use Tag search for these.

**Content published BY an entity, and content ABOUT an entity.** These are two
different filters, not two names for the same thing:

- `source_class` / `evidence_type` — authorship: who **published** the
  content.

  Canonical `source_class` values: `central_bank`, `official_regulator`,
  `legislature`, `government_other`, `company_first_party`, `corporate_blog`,
  `sec_filing`, `earnings_transcript`, `newswire`, `academic_preprint`,
  `peer_reviewed_journal`, `clinical_registry`, `podcast`, `secondary_news`,
  `commentary`. `government` is an umbrella alias. It expands to
  `central_bank` + `official_regulator` + `government_other` + `legislature` on
  the manifest-index path only. Use this filter for "the Fed's own speeches",
  "this company's SEC filings", "central bank commentary".
- `tag_names` (or `tag_ids`) — subject: content **about** an entity or topic,
  regardless of who published it. Use this filter for "everything mentioning
  the Federal Reserve" or "news about this company", including third-party
  coverage.

They compose. `{"tag_names": ["Federal Reserve"], "source_class": ["central_bank"]}`
narrows to the first-party output of the Fed. Drop `source_class` to widen to
everything (anyone's coverage) that mentions the Fed.

Sometimes you already know the exact source or form. For that routing, use
`sec_form_type` (for example `8-K`, `10-Q`) or `source_channel_names` (for
example `scotus-opinions`) instead of either filter. Never substitute an
adjacent form or source channel name.
