CLAUDE.md
CryptoQuant API documentation, built with Mintlify. Config:docs.json. Pages are .mdx;
API reference is generated from OpenAPI specs.
Tab → source map
Sentiment Data tab (Analyst Consensus API) — what is different
- Different host, different key. Base URL
https://consensus.cryptoquant.com/api/v1/, auth isX-API-Key: <YOUR_API_KEY>(or?api_key=), notAuthorization: Bearer. The CryptoQuant Data API key is not accepted there today — do not document it as if it were. Keep the plan rules (No key / Free / Premium / Enterprise, Integration raw-text clearance) as the live API states them; the daily-index-only route with a CryptoQuant key isindicator-api/consensus. - Spec is generated upstream (unbias-app
web/src/lib/openapi.ts). Content changes to parameters / schemas / examples belong there; here,node scripts/import-sentiment-openapi.mjsrefreshesopenapi/sentiment.json(idempotent; add--from <file>for a local build). Prose on thesentiment/*.mdxpages mirrors the formerconsensus.cryptoquant.com/docs/apipage — the consensus site now redirects that URL to/sentiment/overview, so page slugs undersentiment/are external links: rename only with aredirectsentry indocs.json. - Code samples: same five languages and
<YOUR_API_KEY>placeholder as the other specs, but the header isX-API-Key; public (security: []) operations carry no header at all.
MDX rendering gotchas (these break the build silently)
Mintlify compiles descriptions and page bodies as MDX. A raw<...> that isn’t a real
component is parsed as JSX; compilation fails and the entire block renders as raw text
(literal ###, | tables, backticks). Rules:
- Self-close void tags:
<br/>, not<br>. Same for<hr/>,<img ... />. Bare<br>was the cause of the Lightning/Mempool “broken page” reports. - No raw angle-bracket placeholders in prose or frontmatter:
<YOUR_API_KEY>,<token>,List<T>etc. break MDX unless they are inside a code span (`<YOUR_API_KEY>`), a code fence, or written as the entity<. Frontmatterdescription:is compiled too — it must not contain a raw<...>. - Inside fenced/inline code,
<...>and{...}are safe.{/* ... */}is a valid MDX comment.$$ ... \frac{}{} ... $$is KaTeX, not JSX — leave it.
grep -n '<br>' openapi/*.json (expect 0; only <br/>).
Code-sample conventions (keep all three APIs identical)
Everyx-codeSamples / hand-written example uses the same shape and the placeholder
token <YOUR_API_KEY> (never ${access_token}, {access_token}, $CRYPTOQUANT_API_KEY).
- Shell:
curl -X GET "URL" \newline-H "Authorization: Bearer <YOUR_API_KEY>"(-X GETis explicit even though GET is curl’s default; public/no-auth endpoints omit the-H). - JavaScript / NodeJS / Ruby / Python: match the templates already in
openapi/v2.json(auth header included,<YOUR_API_KEY>). Language order: Shell, JavaScript, NodeJS, Ruby, Python.
curl "URL" for a
protected endpoint.
limit parameter (differs by API — do not copy blindly)
Exception:
/btc/flow-indicator/stablecoins-ratio (v1) caps limit at 1000, not 100000 — intentional.
Editing the OpenAPI JSON specs
openapi/v1.json is a GENERATED file — never edit it directly. The nightly
refresh-latency workflow runs node scripts/generate-catalog.mjs, which rebuilds it from
scripts/v1.raw and commits the result: any direct edit to openapi/v1.json (also
catalog/*, legacy-redirect-map.json) is silently reverted within a day. This is exactly
how the Lightning-rename, <br/>, code-sample, and real-example fixes were all lost once.
Instead:
- Content changes (descriptions, examples, code samples, parameters) → edit
scripts/v1.raw, then runnode scripts/generate-catalog.mjsand commit raw + outputs. - Systematic corrections (renames, MDX escaping) → add a transform in
scripts/generate-catalog.mjs(see the “Known content corrections” section: Lightning rename, bare-<br>self-closing). - Keyless runs of the generator keep the cached latency snapshot; only CI (with
CRYPTOQUANT_API_KEY) refreshes it. The generator is idempotent — re-running must produce no diff.
openapi/v2.json is still hand-maintained; it is serialized with
json.dumps(data, indent=1, ensure_ascii=False) and re-dumping is byte-exact. Prefer
surgical string replacement for small edits to keep diffs minimal.
Verifying changes locally
.mdx edits hot-reload. Changes to openapi/*.json require restarting the dev server
(the parsed spec is cached). Confirm a page renders with 0 console errors before claiming done.
Data Changelog (guides/changelog-data.mdx) — where the entries come from
The Changelog tab has two flat pages: guides/changelog (API) and guides/changelog-data (Data).
The Data page is hand-maintained; the 2024-10 → 2026-10 backfill (55 entries, built 2026-10-21)
was compiled once from the sources below. Use the same sources when adding entries.
Entry shape. One <Update> per entity-label rotation (“branching” swap into serving), dated the
day the new data went live. Tags: Labeling + asset (BTC, ETH, Stablecoin & ERC-20, TRON).
API version is a badge, not a tag: <span className="cq-api-badge cq-api-v1">API v1</span>
(CSS in style.css). Rules agreed with the data team: swaps of the same pipeline within 7 days are
merged and dated by the latest; [TEST] runs, ❌-rejected QA, and “follow_up 재검사” are skipped;
entries before the API v2 beta launch (2026-10-08) are v1 only; BTC after that carries v1+v2,
ETH East (v2 pipeline) carries v2 only, XRP/TRON are v1 only.
Swap dates (when did a rotation go live).
Which exchanges changed (label detail).
- West AML RDS (
cq_data_infoprofilerds_west.yaml→aml):btc.integration_address,eth.integration_address— per-addressentity_name,created_at, status (status/eth_status/erc20_status). Aggregatecreated_atbetween two cutoffs. BTC: status is not a filter — PoR labels stayNEWand are counted (44MNEWvs 0.4MVALIDATED); for ETH useVALIDATED.created_atis registration time, not validation time. - East BTC serving labels are
tech_lakehouse_prod.bitcoin.address_cluster.entity_id(address level). Delta time travel gives the label delta of a rotation: count per entityTIMESTAMP AS OFthe previous rotation vs now (joinmetadata.entityfor names). Operator run logs for East rotations live in~/cq_works/etc_projects/eth_migration/local_v2/btc_branching_<date>_log.md.tech_bigquery_foreign_catalog.metadata.labelis a mirror of the same rows. - Slack
#labels: human label requests (Binance POR lists, new cold wallets, Foundry payout addresses) — good for the “why” and for Notion detail pages. - Internal entity names vary (
coinbase pro/coinbase_advanced/coinbase_advenced,okex/okx,huobi global/htx_global); normalise for display.
errorbot, scopes include
channels:history) — token lives in tech-east-airflow-dag/utils/slack.py (pass it via env var,
never copy it here). The bot must be a member of a channel to read it (/invite @errorbot);
pocalertbot has no history scope. RDS/Databricks access via ~/cq_works/cq_data_info
(.venv/bin/python, clients.RdsClient, clients.DatabricksClient).
Not useful for this page. #alarm-entity-qa (reserve threshold alerts), #alarm-exchange-flow-qa
and #alarm-outlier-* (outlier QA), #alarm-address-balance-metrics (dead since 2024-10).