# World

URL: https://docs.hanzo.ai/docs/openapi/world

> A live news feed filtered to what your project cares about.

Also for this capability: **API** · [CLI](https://docs.hanzo.ai/docs/cli/world) · [MCP](https://docs.hanzo.ai/docs/mcp-tools/world) · [SDKs](https://docs.hanzo.ai/docs/sdks)

A live news feed filtered to what your project cares about.

> [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|                |                                        |
| -------------- | -------------------------------------- |
| **Base URL**   | `https://api.hanzo.ai`                 |
| **Operations** | 6                                      |
| **Auth**       | `Authorization: Bearer $HANZO_API_KEY` |

## Specification

> **HIP-1162 · World — The News Feed** — Draft · [read the specification →](https://github.com/hanzoai/hips/blob/7ed737180339cd05b5f2935eafe7f7b83d2449a2/HIPs/hip-1162-world-the-news-feed.md)

`/v1/world` is a live news feed filtered to what a project cares about: GDELT
and host-allowlisted RSS/Atom normalized into one `NewsItem` stream, narrowed
by the project's keyword/region/source pipeline, served over REST and SSE. It
is the Go backend for the World monitor frontend (`hanzoai/world`), replacing
that app's edge functions with an org-scoped, in-binary subsystem. The
implementation is `hanzoai/cloud` `apps/world`.

### Motivation

The World frontend ran its data plane as Vercel edge functions — an
unauthenticated proxy with no tenant, no per-project config that survives, and
a second deployment target beside the cloud. Moving the fetchers into the
binary gives the feed an org, gives the pipeline a durable home, and puts the
SSRF boundary where the rest of the estate's boundaries are enforced.

### Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted
as in RFC 2119.

#### §1 The store

One SQLite store, `world` (`apps/world/store.go:68`), holds the pipeline
config — the feed URLs to poll plus the filters applied to the merged result.
Tenancy is the (org, project) composite primary key and a mandatory
`WHERE org=? AND project=?` on every statement. News items themselves are
never stored: they are fetched, normalized and filtered per request, with a
ten-minute in-memory TTL cache keyed on the upstream identity (feed URL, GDELT
query) — NOT the tenant, because an upstream feed's content is a public value
identical for every org, and the per-project narrowing happens after the cache
read (`apps/world/fetch.go`).

#### §2 The address

Every route is under `/v1/world` (`manifest/apps.go:382`): six operations,
five typed. `GET /v1/world/stream` is the one declared route — an SSE stream
is not a value, so it carries prose beside the wire fact
(`apps/world/stream.go:120`). Two reads are public by design and carry no
tenant data: `GET /v1/world` (the front door naming the product's wires) and
`GET /v1/world/limits` (a plan's gates). Two more wires answer under this
prefix and are NOT served by this binary — `/v1/world/mcp` and `/v1/world/zap`
are carved off by the ingress and answered by world-gw; the generated document
cannot declare them (prose renders only for a route this router serves), so
`GET /v1/world` names them, and that op is the only place they appear.

#### §3 Tenancy

The (org, project) tuple is `principal.Org` + `principal.Project` — the values
the identity boundary minted from the validated bearer (HIP-0026) — never a
query, body or client header. A request with no validated principal is 403,
except the two public reads in §2. The SSE bus filters on org and the stream
loop drops other projects.

#### §4 Money

The surface is free, said in those words: `cloud.Free`
(`plugin/world/main.go`), no meter, no entry in the standing gate. What a plan
changes is limits, not price: rate and alert gates resolve from the
`hanzoai/plans` catalog through the one `world.*` entitlement vocabulary
(`apps/world/entitlement.go`), read, never duplicated.

#### §5 Events and telemetry

It publishes nothing on the bus; a customer's webhooks receive no `world.*`
events. The SSE stream is an in-process fan-out, best-effort and non-blocking
— a slow subscriber is dropped and re-fetches truth from `GET /v1/world/news`,
which MUST remain the source of truth; the stream is a live hint. Beyond the
request span it emits only its own log lines.

#### §6 Upstream

It derives from no third-party OSS: the fetchers are a Go port of this
product's own edge functions (`hanzoai/world` `api/gdelt-doc.js`,
`api/rss-proxy.js`). What it consumes are public data services — the GDELT 2.0
Doc API and the \~180 allowlisted RSS/Atom hosts, ported verbatim from the
frontend's list (`apps/world/allowlist.go`). Persistence is
`github.com/hanzoai/sqlite` (MIT / Apache-2.0 dual).

#### §7 Stage

`beta`: a vertical application (an intelligence monitor), not part of the
self-service agentic-OS core. The manifest row declares it
(`manifest/apps.go:382`, `Stage: Beta`), so the capability is reached by flag
(HIP-0139 §8).

### Rationale

Filtering after a global cache, rather than caching per tenant, was chosen
because the cached value is public news and the tenant-specific part is the
projection: caching per org multiplies identical upstream fetches by the
number of orgs and buys no isolation the filter does not already provide. The
pipeline store keeps only config because news is the upstream's fact — storing
items would make this a second archive that ages, instead of a view that is as
fresh as its sources.

### Security Considerations

The RSS fetcher is an SSRF boundary: a pipeline's feed URL is
attacker-writable config that this binary will dereference from inside the
cluster. The host MUST be on the allowlist, enforced at both the PUT write
boundary and at fetch time, including on every redirect target
(`apps/world/world.go:35`) — checking only at write time leaves a redirect
from an allowed host as a free pass to an internal address. The other exposure
is a cross-tenant pipeline read, closed by §3's composite key and the
never-caller-supplied org.

## Four surfaces

| Surface  | Reaches this capability as                    | Coverage                                                                  |
| -------- | --------------------------------------------- | ------------------------------------------------------------------------- |
| **REST** | `world` at its own prefix                     | 6 operations                                                              |
| **CLI**  | `hanzo world …`                               | 6 of 6                                                                    |
| **SDK**  | `WorldApi` in every published client          | 6 methods                                                                 |
| **MCP**  | tool `world` on `https://api.hanzo.ai/v1/mcp` | 5 operations, 1 under the document's own id — ask `describe` for the rest |

## Quickstart

```bash
export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys
```

Then the first call — a read that needs nothing but the key. `GET /v1/world`, operation `get_world`:

**CLI**

```bash
hanzo world get
```

**SDK**

**.ts**

```ts
import { Configuration, WorldApi } from 'hanzoai';

const api = new WorldApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getWorld();
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import WorldApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = WorldApi(client).get_world()
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.WorldAPI.GetWorld(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, world_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = world_api::get_world(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.WorldApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new WorldApi(client).getWorld();
```

**HTTP**

```bash
curl https://api.hanzo.ai/v1/world \
  -H "Authorization: Bearer $HANZO_API_KEY"
```

**MCP**

MCP reaches **world** through the `world` tool, which names its 5 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "list_worlds"
         }
       }
     }'
```

Answers `200` with `object` — ok.

## Endpoints

| Endpoint                                                                                | What it does                                                                                                                                                                                                                                                                                                    |
| --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`GET /v1/world/limits`](https://docs.hanzo.ai/docs/openapi/world/get-world-limits)     | Echoes a World plan's rate limits, alert quota and model-API grant, read straight from the live @hanzo/plans catalog, so agents and dashboards configure themselves against the catalog instead of hardcoding tier numbers.                                                                                     |
| [`GET /v1/world/news`](https://docs.hanzo.ai/docs/openapi/world/get-world-news)         | Returns the caller's merged world-news feed: every source their project's pipeline names — GDELT once per keyword, plus each allowlisted RSS or Atom feed — fetched concurrently, narrowed by the pipeline's keyword/region/source filters, deduplicated by link and sorted freshest first, capped at 50 items. |
| [`GET /v1/world/pipeline`](https://docs.hanzo.ai/docs/openapi/world/get-world-pipeline) | Returns the caller project's news pipeline: which feeds it reads and how the merged result is filtered.                                                                                                                                                                                                         |
| [`PUT /v1/world/pipeline`](https://docs.hanzo.ai/docs/openapi/world/put-world-pipeline) | Replaces the caller project's news pipeline and returns what was stored.                                                                                                                                                                                                                                        |
| [`GET /v1/world/stream`](https://docs.hanzo.ai/docs/openapi/world/get-world-stream)     | Live news refreshes for the caller's org and project, as Server-Sent Events.                                                                                                                                                                                                                                    |
| [`GET /v1/world`](https://docs.hanzo.ai/docs/openapi/world/get-world)                   | Answers GET /v1/world — the product's public endpoint, naming every wire this surface answers on.                                                                                                                                                                                                               |

---

[All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
