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

# AGENTS

# gdelt-cloud-docs — Agent Guide

The Mintlify site behind **docs.gdeltcloud.com**. It lives inside the `gdelt-cloud` monorepo and
auto-deploys on push to `main`.

This file is internal. It is deliberately **not** in `docs.json`, so it is excluded from the built
site and from the `llms.txt` Mintlify generates from the nav — a crawling model should read the
product, not our authoring conventions.

***

## ★ The spec and the reference tier are GENERATED — never hand-edit them

| File                             | Emitted by                                      |
| -------------------------------- | ----------------------------------------------- |
| `api-reference/openapi-v2.json`  | `nextjs_/scripts/registry/emit-openapi.ts`      |
| `reference/enums.mdx`            | `nextjs_/scripts/registry/emit-docs.ts`         |
| `reference/parameters.mdx`       | `nextjs_/scripts/registry/emit-docs.ts`         |
| `reference/taxonomy-complete.md` | `nextjs_/scripts/registry/emit-taxonomy-doc.ts` |

They come from the contract registry at `nextjs_/lib/api/v2/registry/` — the same objects the server
imports **at request time** to build its param allowlist and validate enums. That is what makes them
trustworthy: a generated page cannot describe a value the API rejects, or omit one it accepts.

**To change one, edit the descriptor and run `pnpm gen:contract` in `nextjs_/`.** `prebuild` runs
the gate, so a hand-edit fails the Vercel deploy with the fix command in the error.

Hand-write **prose** pages only. The emitter owns numbers and tables; humans own sentences.

### Never restate a value list in a hand-written page

Not because duplication is untidy — because it *drifts*, and drifted docs are worse than absent
ones. Measured on 2026-08-05 against a running server, hand-written pages claimed:

* `category` is "an open vocabulary" that "also accepts CAMEO+ code strings" — `category=TE01`
  returns `400 INVALID_CATEGORY`.
* story categories are "not a fixed enum" with ten prose theme buckets — the real vocabulary is 11
  closed snake\_case values, and a display label 400s.
* CAMEO+ has 8 domains — it has 10; `CRIME` and `DEMOGRAPHIC` both work and were undiscoverable.
* coverage starts January 2025 — that month holds **11 events**. Usable coverage starts March 2026
  (15,744 events). True, and a lie.

Link to `/reference/enums` instead. It is generated, and it carries the real rejection code per
vocabulary (`INVALID_ENUM`, `INVALID_CATEGORY`, `INVALID_REGION`, `INVALID_CONTINENT` — they differ).

### Open vocabularies are not enums

`reference/enums.mdx` renders three value-spaces in visibly different language, and the difference is
load-bearing: **closed** ("the authoritative set; anything else 400s"), **observed** ("a dated
MEASUREMENT; the API accepts values outside this list"), **identifier** ("a key, not a list").

The spec once declared a closed 13-value `form_type` enum against a corpus holding **264**, where
`424B2` alone was 21% of a 30-day window — wrong in both directions with a generator's authority
behind it. Never describe an observed vocabulary as though it were closed.

***

## Mintlify gotchas that cost real time

1. **Inline `{ "openapi": "GET /path" }` refs only resolve when the spec is declared at the tab or
   group level.** A global `api.openapi` array is **not** enough — without the tab/group
   declaration every inline ref silently generates nothing. We declare it at the tab level *and* on
   each group holding inline refs.
2. **Generated page slugs come from the operation's OpenAPI `tags`, not the nav group name.** An op
   tagged `Entities` lands at `/api-reference/entities/...` wherever you place it. So tag and group
   names must avoid `&` and other URL-hostile characters — an `&` breaks slug generation and the
   pages silently disappear. Use "and".
3. **Only top-level groups always expand;** nested groups honour `"expanded": false`.
4. **A page not in `docs.json` is not built.** An `.mdx` on disk with no nav entry is invisible, and
   a spec operation with no nav entry returns 200 at its slug while nothing links to it — five had
   accumulated that way, including three Atlas endpoints `index.mdx` tells readers to call. Guarded
   now by `nextjs_/tests/docs/contract-doctrine.test.ts`, both for operations and for generated MDX.
5. **`mint dev` ignores `PORT`.** It probes 3000, then 3001. Check which one it actually bound.

## Before pushing

```bash theme={null}
mint openapi-check api-reference/openapi-v2.json
node scripts/validate-v2-openapi.mjs
mint dev
mint broken-links
```

And in `nextjs_/`: `npx vitest run tests/docs` — the reachability, banner and honesty-label guards
all live there and ride the existing suite.

## Layout

```
index.mdx quickstart.mdx authentication.mdx   entry pages
api-reference/    v2.mdx concepts.mdx taxonomy.mdx cookbook.mdx + openapi-v2.json (GENERATED)
reference/        GENERATED value/parameter/taxonomy tier
features/         methodology and product explanations
metrics/          the four event metrics — every defining page imports snippets/metric-disclosure.mdx
guides/           workflow guides by analyst job
developers/       keys, orgs, plans
mcp/              MCP server docs, tools, skills, integrations
snippets/         shared MDX fragments — one telling, imported
```

**One canonical telling per fact.** When you find a second copy, delete it and import or link the
first. The metric reliability table existed twice with different numbers in the copy three pages
imported; the duplication is the only reason anyone noticed, and also the reason it stayed wrong.
