diff --git a/apps/developer-hub/.prettierignore b/apps/developer-hub/.prettierignore
index 2d76e1aff0..f581727d6f 100644
--- a/apps/developer-hub/.prettierignore
+++ b/apps/developer-hub/.prettierignore
@@ -11,4 +11,5 @@ build/
node_modules/
package.json
tsconfig*.json
-content/docs/price-feeds/core/use-real-time-data/pull-integration/ton.mdx
\ No newline at end of file
+content/docs/price-feeds/core/use-real-time-data/pull-integration/ton.mdx
+content/docs/api-reference/**
\ No newline at end of file
diff --git a/apps/developer-hub/README.md b/apps/developer-hub/README.md
index ace6a547c0..ba63c3282d 100644
--- a/apps/developer-hub/README.md
+++ b/apps/developer-hub/README.md
@@ -120,10 +120,96 @@ Update the `meta.json` file in each section to control navigation. Example:
- Use proper heading hierarchy (h2, h3, etc.)
- Link to related documentation when relevant
+## API Reference Generation
+
+The API reference documentation is automatically generated from OpenAPI specifications using the `scripts/generate-docs.ts` script. This script converts OpenAPI specs (from services like Hermes and Fortuna) into MDX documentation files.
+
+### What It Does
+
+The script performs the following steps:
+
+1. **File Generation**: Uses `fumadocs-openapi` to convert OpenAPI specs into MDX files
+
+ - Each API endpoint becomes a separate MDX file
+ - Files are organized by product (e.g., `pyth-core`, `entropy`) and service (e.g., `hermes`, `fortuna`)
+
+2. **Meta File Generation**: Creates `meta.json` files for navigation
+
+ - Root `meta.json` for the API reference section
+ - Product-level `meta.json` files (e.g., `pyth-core/meta.json`)
+ - Service-level `meta.json` files (e.g., `pyth-core/hermes/meta.json`)
+
+3. **Post-Processing**: Customizes generated files to match our documentation structure
+ - Updates MDX frontmatter titles to use endpoint paths instead of operation IDs
+ - Rewrites `index.mdx` files to use `APICard` components with proper formatting
+
+### When to Run
+
+The script runs automatically during the build process (`pnpm turbo run build`). You typically don't need to run it manually unless:
+
+- You've updated an OpenAPI specification URL
+- You've added a new service to the configuration
+- You want to regenerate docs without doing a full build
+
+### Manual Execution
+
+To run the script manually:
+
+```bash
+pnpm generate:docs
+```
+
+This will:
+
+- Fetch OpenAPI specs from the configured URLs
+- Generate MDX files in `content/docs/api-reference/`
+- Create/update all `meta.json` navigation files
+- Post-process files to customize titles and index pages
+
+### Configuration
+
+To add a new API service, edit `src/lib/openapi.ts` and add an entry to the `products` object:
+
+```typescript
+export const products = {
+ // ... existing services ...
+ newService: {
+ name: "newService",
+ product: "product-category", // e.g., "pyth-core" or "entropy"
+ openApiUrl: "https://api.example.com/docs/openapi.json",
+ },
+};
+```
+
+After adding a new service:
+
+1. Run `pnpm generate:docs` to generate the documentation
+2. The new service will appear in the API reference navigation
+
+### Generated Files
+
+All generated files are written to `content/docs/api-reference/`:
+
+```
+content/docs/api-reference/
+├── meta.json # Root navigation
+├── pyth-core/
+│ ├── meta.json # Product navigation
+│ └── hermes/
+│ ├── meta.json # Service navigation
+│ ├── index.mdx # Service overview page
+│ └── *.mdx # Individual endpoint pages
+└── entropy/
+ └── ...
+```
+
+**Important**: Generated files should not be edited manually. Any changes will be overwritten the next time the script runs. If you need to customize the documentation, modify the OpenAPI specification or the generation script itself.
+
## Available Commands
- `pnpm turbo run start:dev` - Start development server
-- `pnpm turbo run build` - Build the project
+- `pnpm turbo run build` - Build the project (includes API reference generation)
+- `pnpm generate:docs` - Generate API reference documentation manually
- `pnpm turbo run fix:format` - Format code with Prettier
- `pnpm turbo run fix:lint:eslint` - Fix ESLint issues
- `pnpm turbo run test:format` - Check formatting
diff --git a/apps/developer-hub/content/docs/openapi/fortuna/chain_ids.mdx b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/chain_ids.mdx
similarity index 63%
rename from apps/developer-hub/content/docs/openapi/fortuna/chain_ids.mdx
rename to apps/developer-hub/content/docs/api-reference/entropy/fortuna/chain_ids.mdx
index 48444d5524..ff9dae3b8c 100644
--- a/apps/developer-hub/content/docs/openapi/fortuna/chain_ids.mdx
+++ b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/chain_ids.mdx
@@ -1,5 +1,5 @@
---
-title: Get the list of supported chain ids
+title: "/v1/chains"
description: Get the list of supported chain ids
full: true
_openapi:
@@ -14,9 +14,4 @@ _openapi:
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/openapi/fortuna/explorer.mdx b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/explorer.mdx
similarity index 81%
rename from apps/developer-hub/content/docs/openapi/fortuna/explorer.mdx
rename to apps/developer-hub/content/docs/api-reference/entropy/fortuna/explorer.mdx
index 8e8b4fd387..afee2b8223 100644
--- a/apps/developer-hub/content/docs/openapi/fortuna/explorer.mdx
+++ b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/explorer.mdx
@@ -1,5 +1,5 @@
---
-title: Returns the logs of all requests captured by the keeper.
+title: "/v1/logs"
description: >-
Returns the logs of all requests captured by the keeper.
@@ -32,9 +32,4 @@ _openapi:
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/entropy/fortuna/index.mdx b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/index.mdx
new file mode 100644
index 0000000000..6b6dbf7f31
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/index.mdx
@@ -0,0 +1,9 @@
+---
+title: Overview
+---
+
+
+
+
+
+
diff --git a/apps/developer-hub/content/docs/api-reference/entropy/fortuna/meta.json b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/meta.json
new file mode 100644
index 0000000000..13ec2badc5
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/meta.json
@@ -0,0 +1,9 @@
+{
+ "title": "Fortuna",
+ "pages": [
+ "index",
+ "chain_ids",
+ "revelation",
+ "explorer"
+ ]
+}
diff --git a/apps/developer-hub/content/docs/openapi/fortuna/revelation.mdx b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/revelation.mdx
similarity index 82%
rename from apps/developer-hub/content/docs/openapi/fortuna/revelation.mdx
rename to apps/developer-hub/content/docs/api-reference/entropy/fortuna/revelation.mdx
index a3e8711342..4d14f92027 100644
--- a/apps/developer-hub/content/docs/openapi/fortuna/revelation.mdx
+++ b/apps/developer-hub/content/docs/api-reference/entropy/fortuna/revelation.mdx
@@ -1,5 +1,5 @@
---
-title: Reveal the random value for a given sequence number and blockchain.
+title: "/v1/chains/{chain_id}/revelations/{sequence}"
description: >-
Reveal the random value for a given sequence number and blockchain.
@@ -44,11 +44,4 @@ _openapi:
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/entropy/meta.json b/apps/developer-hub/content/docs/api-reference/entropy/meta.json
new file mode 100644
index 0000000000..2c9bc438b8
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/entropy/meta.json
@@ -0,0 +1,6 @@
+{
+ "title": "Entropy",
+ "pages": [
+ "fortuna"
+ ]
+}
diff --git a/apps/developer-hub/content/docs/api-reference/index.mdx b/apps/developer-hub/content/docs/api-reference/index.mdx
new file mode 100644
index 0000000000..a30a3d26c4
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/index.mdx
@@ -0,0 +1,34 @@
+---
+title: API Reference
+description: Complete API reference for Pyth Network services
+---
+
+import { IntegrationCard } from "../../../src/components/IntegrationCard";
+import { DiceSix, Database } from "@phosphor-icons/react/dist/ssr";
+
+Welcome to the Pyth Network API Reference. Explore REST APIs for our core services.
+
+## Entropy
+
+
+ }
+ colorScheme="green"
+ />
+
+
+## Pyth Core
+
+
+ }
+ colorScheme="blue"
+ />
+
+
diff --git a/apps/developer-hub/content/docs/api-reference/meta.json b/apps/developer-hub/content/docs/api-reference/meta.json
new file mode 100644
index 0000000000..cbf2de12c2
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/meta.json
@@ -0,0 +1,9 @@
+{
+ "root": true,
+ "title": "API Reference",
+ "icon": "Code",
+ "pages": [
+ "entropy",
+ "pyth-core"
+ ]
+}
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_price_feed.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_price_feed.mdx
new file mode 100644
index 0000000000..9f7af043ad
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_price_feed.mdx
@@ -0,0 +1,33 @@
+---
+title: "/api/get_price_feed"
+description: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a price update for a price feed with a specific timestamp
+
+
+ Given a price feed id and timestamp, retrieve the Pyth price update closest to
+ that timestamp.
+full: true
+_openapi:
+ method: GET
+ route: /api/get_price_feed
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a price update for a price feed with a specific timestamp
+
+
+ Given a price feed id and timestamp, retrieve the Pyth price update
+ closest to that timestamp.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa.mdx
new file mode 100644
index 0000000000..63c0d18297
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa.mdx
@@ -0,0 +1,33 @@
+---
+title: "/api/get_vaa"
+description: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a VAA for a price feed with a specific timestamp
+
+
+ Given a price feed id and timestamp, retrieve the Pyth price update closest to
+ that timestamp.
+full: true
+_openapi:
+ method: GET
+ route: /api/get_vaa
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a VAA for a price feed with a specific timestamp
+
+
+ Given a price feed id and timestamp, retrieve the Pyth price update
+ closest to that timestamp.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa_ccip.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa_ccip.mdx
new file mode 100644
index 0000000000..a5945007bf
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/get_vaa_ccip.mdx
@@ -0,0 +1,39 @@
+---
+title: "/api/get_vaa_ccip"
+description: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a VAA for a price feed using CCIP
+
+
+ This endpoint accepts a single argument which is a hex-encoded byte string of
+ the following form:
+
+ ``
+full: true
+_openapi:
+ method: GET
+ route: /api/get_vaa_ccip
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/updates/price/{publish_time} instead**
+
+
+ Get a VAA for a price feed using CCIP
+
+
+ This endpoint accepts a single argument which is a hex-encoded byte
+ string of the following form:
+
+ ``
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/index.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/index.mdx
new file mode 100644
index 0000000000..61556e66ed
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/index.mdx
@@ -0,0 +1,18 @@
+---
+title: Overview
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_feeds.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_feeds.mdx
new file mode 100644
index 0000000000..3d4b3fa5af
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_feeds.mdx
@@ -0,0 +1,33 @@
+---
+title: "/api/latest_price_feeds"
+description: >-
+ **Deprecated: use /v2/updates/price/latest instead**
+
+
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price for each
+ price feed.
+full: true
+_openapi:
+ method: GET
+ route: /api/latest_price_feeds
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/updates/price/latest instead**
+
+
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price
+ for each price feed.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_updates.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_updates.mdx
new file mode 100644
index 0000000000..7efae3ad15
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_price_updates.mdx
@@ -0,0 +1,27 @@
+---
+title: "/v2/updates/price/latest"
+description: >-
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price for each
+ price feed.
+full: true
+_openapi:
+ method: GET
+ route: /v2/updates/price/latest
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price
+ for each price feed.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_publisher_stake_caps.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_publisher_stake_caps.mdx
new file mode 100644
index 0000000000..0a80abd6aa
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_publisher_stake_caps.mdx
@@ -0,0 +1,17 @@
+---
+title: "/v2/updates/publisher_stake_caps/latest"
+description: Get the most recent publisher stake caps update data.
+full: true
+_openapi:
+ method: GET
+ route: /v2/updates/publisher_stake_caps/latest
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: Get the most recent publisher stake caps update data.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_twaps.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_twaps.mdx
new file mode 100644
index 0000000000..b2f61c001a
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_twaps.mdx
@@ -0,0 +1,27 @@
+---
+title: "/v2/updates/twap/{window_seconds}/latest"
+description: >-
+ Get the latest TWAP by price feed id with a custom time window.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth TWAP price for
+ each price feed.
+full: true
+_openapi:
+ method: GET
+ route: /v2/updates/twap/{window_seconds}/latest
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Get the latest TWAP by price feed id with a custom time window.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth TWAP
+ price for each price feed.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_vaas.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_vaas.mdx
new file mode 100644
index 0000000000..dc35023480
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/latest_vaas.mdx
@@ -0,0 +1,43 @@
+---
+title: "/api/latest_vaas"
+description: >-
+ **Deprecated: use /v2/updates/price/latest instead**
+
+
+ Get VAAs for a set of price feed ids.
+
+
+ Given a collection of price feed ids, retrieve the latest VAA for each. The
+ returned VAA(s) can
+
+ be submitted to the Pyth contract to update the on-chain price. If VAAs are
+ not found for every
+
+ provided price ID the call will fail.
+full: true
+_openapi:
+ method: GET
+ route: /api/latest_vaas
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/updates/price/latest instead**
+
+
+ Get VAAs for a set of price feed ids.
+
+
+ Given a collection of price feed ids, retrieve the latest VAA for
+ each. The returned VAA(s) can
+
+ be submitted to the Pyth contract to update the on-chain price. If
+ VAAs are not found for every
+
+ provided price ID the call will fail.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/meta.json b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/meta.json
new file mode 100644
index 0000000000..8787df25f3
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/meta.json
@@ -0,0 +1,18 @@
+{
+ "title": "Hermes",
+ "pages": [
+ "index",
+ "get_price_feed",
+ "get_vaa",
+ "get_vaa_ccip",
+ "latest_price_feeds",
+ "latest_vaas",
+ "price_feed_ids",
+ "price_feeds_metadata",
+ "latest_price_updates",
+ "price_stream_sse_handler",
+ "timestamp_price_updates",
+ "latest_publisher_stake_caps",
+ "latest_twaps"
+ ]
+}
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feed_ids.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feed_ids.mdx
new file mode 100644
index 0000000000..9a07572bbc
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feed_ids.mdx
@@ -0,0 +1,33 @@
+---
+title: "/api/price_feed_ids"
+description: >-
+ **Deprecated: use /v2/price_feeds instead**
+
+
+ Get the set of price feed IDs.
+
+
+ This endpoint fetches all of the price feed IDs for which price updates can be
+ retrieved.
+full: true
+_openapi:
+ method: GET
+ route: /api/price_feed_ids
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ **Deprecated: use /v2/price_feeds instead**
+
+
+ Get the set of price feed IDs.
+
+
+ This endpoint fetches all of the price feed IDs for which price
+ updates can be retrieved.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feeds_metadata.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feeds_metadata.mdx
new file mode 100644
index 0000000000..d12730307e
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_feeds_metadata.mdx
@@ -0,0 +1,31 @@
+---
+title: "/v2/price_feeds"
+description: >-
+ Get the set of price feeds.
+
+
+ This endpoint fetches all price feeds from the Pyth network. It can be
+ filtered by asset type
+
+ and query string.
+full: true
+_openapi:
+ method: GET
+ route: /v2/price_feeds
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Get the set of price feeds.
+
+
+ This endpoint fetches all price feeds from the Pyth network. It can be
+ filtered by asset type
+
+ and query string.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_stream_sse_handler.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_stream_sse_handler.mdx
new file mode 100644
index 0000000000..21dceff295
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/price_stream_sse_handler.mdx
@@ -0,0 +1,33 @@
+---
+title: "/v2/updates/price/stream"
+description: >-
+ SSE route handler for streaming price updates.
+
+
+ The connection will automatically close after 24 hours to prevent resource
+ leaks.
+
+ Clients should implement reconnection logic to maintain continuous price
+ updates.
+full: true
+_openapi:
+ method: GET
+ route: /v2/updates/price/stream
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ SSE route handler for streaming price updates.
+
+
+ The connection will automatically close after 24 hours to prevent
+ resource leaks.
+
+ Clients should implement reconnection logic to maintain continuous
+ price updates.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/timestamp_price_updates.mdx b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/timestamp_price_updates.mdx
new file mode 100644
index 0000000000..297a41392a
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/hermes/timestamp_price_updates.mdx
@@ -0,0 +1,27 @@
+---
+title: "/v2/updates/price/{publish_time}"
+description: >-
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price for each
+ price feed.
+full: true
+_openapi:
+ method: GET
+ route: /v2/updates/price/{publish_time}
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Get the latest price updates by price feed id.
+
+
+ Given a collection of price feed ids, retrieve the latest Pyth price
+ for each price feed.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/developer-hub/content/docs/api-reference/pyth-core/meta.json b/apps/developer-hub/content/docs/api-reference/pyth-core/meta.json
new file mode 100644
index 0000000000..34888f85e1
--- /dev/null
+++ b/apps/developer-hub/content/docs/api-reference/pyth-core/meta.json
@@ -0,0 +1,6 @@
+{
+ "title": "Pyth Core",
+ "pages": [
+ "hermes"
+ ]
+}
diff --git a/apps/developer-hub/content/docs/meta.json b/apps/developer-hub/content/docs/meta.json
index 0d89d9cdc8..6af1ac7229 100644
--- a/apps/developer-hub/content/docs/meta.json
+++ b/apps/developer-hub/content/docs/meta.json
@@ -1,3 +1,3 @@
{
- "pages": ["price-feeds", "express-relay", "entropy"]
+ "pages": ["price-feeds", "express-relay", "entropy", "api-reference"]
}
diff --git a/apps/developer-hub/content/docs/openapi/fortuna/index.mdx b/apps/developer-hub/content/docs/openapi/fortuna/index.mdx
deleted file mode 100644
index 483df2b0b0..0000000000
--- a/apps/developer-hub/content/docs/openapi/fortuna/index.mdx
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title: Overview
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-
-
-
-
-
diff --git a/apps/developer-hub/package.json b/apps/developer-hub/package.json
index cf6cea95e8..9317e02ecb 100644
--- a/apps/developer-hub/package.json
+++ b/apps/developer-hub/package.json
@@ -18,7 +18,7 @@
"test:lint:eslint": "eslint . --max-warnings 0",
"test:lint:stylelint": "stylelint 'src/**/*.scss' --max-warnings 0",
"test:types": "tsc",
- "generate:docs": "bun ./scripts/generate-docs.ts"
+ "generate:docs": "tsx ./scripts/generate-docs.ts"
},
"dependencies": {
"@phosphor-icons/react": "catalog:",
@@ -48,6 +48,7 @@
"remark-math": "^6.0.0",
"remark-mdx": "^3.1.1",
"shiki": "catalog:",
+ "tsx": "catalog:",
"viem": "catalog:",
"zod": "catalog:",
"zod-validation-error": "catalog:"
diff --git a/apps/developer-hub/scripts/generate-docs.ts b/apps/developer-hub/scripts/generate-docs.ts
index 03e0a9ac5f..d1d624d894 100644
--- a/apps/developer-hub/scripts/generate-docs.ts
+++ b/apps/developer-hub/scripts/generate-docs.ts
@@ -1,50 +1,580 @@
+/**
+ * API Reference Documentation Generator
+ *
+ * This script automatically generates API reference documentation from OpenAPI specifications.
+ * It converts OpenAPI specs (from services like Hermes and Fortuna) into MDX documentation
+ * files that are used by the Fumadocs documentation system.
+ *
+ *
+ * ## Usage
+ *
+ * This script runs automatically during the build process. To run it manually:
+ *
+ * ```bash
+ * pnpm generate:docs
+ * ```
+ *
+ * ## Configuration
+ *
+ * To add a new API service, add it to `src/lib/openapi.ts` in the `products` object.
+ * Each service needs:
+ * - `name`: Service identifier (e.g., "hermes")
+ * - `product`: Product category (e.g., "pyth-core")
+ * - `openApiUrl`: URL to the OpenAPI specification JSON
+ *
+ */
+
+import * as fs from "node:fs/promises";
+import path from "node:path";
+
import { generateFiles } from "fumadocs-openapi";
+import { createOpenAPI } from "fumadocs-openapi/server";
-import { openapi, products } from "../src/lib/openapi";
-
-const outDir = "./content/docs/openapi/";
-
-export async function generateDocs() {
- await generateFiles({
- input: openapi,
- output: outDir,
- per: "operation",
- name: (output, document) => {
- // Extract product name from the OpenAPI document info
- const productName = getProductName(document.info.title);
-
- if (output.type === "operation") {
- const operation =
- document.paths?.[output.item.path]?.[output.item.method];
- const operationId =
- operation?.operationId ??
- output.item.path.replaceAll(/[^a-zA-Z0-9]/g, "_");
- return `${productName}/${operationId}`;
- }
+import { products } from "../src/lib/openapi";
- return `${productName}/webhooks/${output.item.name}`;
- },
- index: {
- url: {
- baseUrl: "/openapi/",
- contentDir: "./content/docs/openapi",
+const OUTPUT_DIR = "./content/docs/api-reference/";
+
+const generatedEndpoints: Record = {};
+
+type ApiCardData = {
+ href: string;
+ route: string;
+ method: string;
+ description: string;
+};
+
+type MetaFile = {
+ root?: boolean;
+ title: string;
+ icon?: string;
+ pages: string[];
+};
+
+export async function generateDocs(): Promise {
+ // eslint-disable-next-line no-console
+ console.log("Starting API reference documentation generation...\n");
+
+ await generateMdxFilesFromOpenApi();
+
+ await generateMetaFiles();
+
+ await generateApiReferenceIndex();
+
+ await updateMdxTitles();
+
+ await updateIndexCards();
+
+ // eslint-disable-next-line no-console
+ console.log("\nDocumentation generation complete!");
+}
+
+// ============================================================================
+// File Generation
+// ============================================================================
+
+/**
+ * Generates MDX documentation files from OpenAPI specifications.
+ *
+ * Processes each service separately to ensure:
+ * - Index files only contain endpoints from that specific service
+ * - Each service can have its own OpenAPI spec URL
+ * - Generated files are organized by product/service hierarchy
+ *
+ * For each service:
+ * - Creates an OpenAPI instance from the service's spec URL
+ * - Generates one MDX file per API operation
+ * - Tracks generated operation IDs for later use in meta files
+ * - Creates an index.mdx file listing all endpoints
+ */
+async function generateMdxFilesFromOpenApi(): Promise {
+ // eslint-disable-next-line no-console
+ console.log("Generating MDX files from OpenAPI specifications...");
+
+ for (const [serviceName, config] of Object.entries(products)) {
+ // eslint-disable-next-line no-console
+ console.log(`\n Processing service: ${serviceName}`);
+
+ generatedEndpoints[serviceName] = [];
+
+ const serviceOpenapi = createOpenAPI({
+ input: [config.openApiUrl],
+ });
+
+ // Generate MDX files using fumadocs-openapi
+ await generateFiles({
+ input: serviceOpenapi,
+ output: OUTPUT_DIR,
+ per: "operation", // One file per API operation
+ name: (output, document) => {
+ // Generate file name based on operation type
+ if (output.type === "operation") {
+ return generateOperationFileName(
+ output,
+ document,
+ serviceName,
+ config.product,
+ );
+ }
+
+ return `${config.product}/${serviceName}/webhooks/${output.item.name}`;
},
- items: Object.keys(products).map((productName) => ({
- path: `${productName}/index.mdx`,
- })),
+ frontmatter: (context) => {
+ const ctx = context as { type?: string; path?: string };
+ if (ctx.type === "operation" && ctx.path) {
+ return {
+ title: ctx.path,
+ };
+ }
+ return {};
+ },
+ index: {
+ url: {
+ baseUrl: "/api-reference/",
+ contentDir: "./content/docs/api-reference",
+ },
+ items: [
+ {
+ path: `${config.product}/${serviceName}/index.mdx`,
+ },
+ ],
+ },
+ });
+ }
+}
+
+function generateOperationFileName(
+ output: { item: { path: string; method: string } },
+ document: unknown,
+ serviceName: string,
+ productName: string,
+): string {
+ const doc = document as {
+ paths?: Record<
+ string,
+ Record | undefined
+ >;
+ };
+ const operation = doc.paths?.[output.item.path]?.[output.item.method];
+
+ const operationId =
+ operation?.operationId ?? output.item.path.replaceAll(/[^a-zA-Z0-9]/g, "_");
+
+ // Track this endpoint for meta file generation
+ generatedEndpoints[serviceName]?.push(operationId);
+
+ // eslint-disable-next-line no-console
+ console.log(` ✓ ${operationId}`);
+
+ // Return file path: product/service/operationId
+ return `${productName}/${serviceName}/${operationId}`;
+}
+
+/**
+ * Generates all meta.json files for navigation structure.
+ *
+ * Creates a three-level hierarchy:
+ * 1. Root meta.json: Lists all product categories (e.g., pyth-core, entropy)
+ * 2. Product meta.json: Lists all services in that product (e.g., hermes in pyth-core)
+ * 3. Service meta.json: Lists all endpoints in that service (e.g., get_price_feed in hermes)
+ *
+ * These meta.json files are used by Fumadocs to build the navigation sidebar.
+ */
+async function generateMetaFiles(): Promise {
+ // eslint-disable-next-line no-console
+ console.log("\nGenerating meta.json navigation files...");
+
+ const productGroups = groupServicesByProduct();
+
+ await generateRootMetaFile(productGroups);
+
+ await generateProductAndServiceMetaFiles(productGroups);
+}
+
+function groupServicesByProduct(): Record {
+ const productGroups: Record = {};
+
+ for (const [serviceName, config] of Object.entries(products)) {
+ productGroups[config.product] ??= [];
+ productGroups[config.product]?.push(serviceName);
+ }
+
+ return productGroups;
+}
+
+async function generateRootMetaFile(
+ productGroups: Record,
+): Promise {
+ const rootMeta: MetaFile = {
+ root: true,
+ title: "API Reference",
+ icon: "Code",
+ pages: Object.keys(productGroups),
+ };
+
+ await writeJson(path.join(OUTPUT_DIR, "meta.json"), rootMeta);
+ // eslint-disable-next-line no-console
+ console.log(" ✓ api-reference/meta.json");
+}
+
+async function generateProductAndServiceMetaFiles(
+ productGroups: Record,
+): Promise {
+ for (const [productName, services] of Object.entries(productGroups)) {
+ const productMeta: MetaFile = {
+ title: formatProductTitle(productName),
+ pages: services,
+ };
+
+ const productDir = path.join(OUTPUT_DIR, productName);
+ await fs.mkdir(productDir, { recursive: true });
+ await writeJson(path.join(productDir, "meta.json"), productMeta);
+ // eslint-disable-next-line no-console
+ console.log(` ✓ ${productName}/meta.json`);
+
+ // Generate service-level meta.json files
+ for (const serviceName of services) {
+ const endpoints = generatedEndpoints[serviceName] ?? [];
+ const serviceMeta: MetaFile = {
+ title: formatServiceTitle(serviceName),
+ pages: ["index", ...endpoints],
+ };
+
+ const serviceDir = path.join(productDir, serviceName);
+ await writeJson(path.join(serviceDir, "meta.json"), serviceMeta);
+ // eslint-disable-next-line no-console
+ console.log(` ✓ ${productName}/${serviceName}/meta.json`);
+ }
+ }
+}
+
+function formatProductTitle(productName: string): string {
+ return productName
+ .split("-")
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(" ");
+}
+
+function formatServiceTitle(serviceName: string): string {
+ return serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
+}
+
+/**
+ * Generates the API Reference index page (content/docs/api-reference/index.mdx).
+ *
+ * Creates a page that lists all products and their associated services using
+ * IntegrationCard components in a simple grid layout.
+ */
+async function generateApiReferenceIndex(): Promise {
+ // eslint-disable-next-line no-console
+ console.log("\nGenerating API Reference index page...");
+
+ const productGroups = groupServicesByProduct();
+
+ // Map service names to their configurations and metadata
+ const serviceMetadata: Record<
+ string,
+ {
+ name: string;
+ product: string;
+ icon: string;
+ colorScheme: "green" | "blue" | "purple" | "yellow";
+ description: string;
+ }
+ > = {
+ fortuna: {
+ name: "fortuna",
+ product: "entropy",
+ icon: "DiceSix",
+ colorScheme: "green",
+ description: "Random number generation API with callback support",
},
- });
+ hermes: {
+ name: "hermes",
+ product: "pyth-core",
+ icon: "Database",
+ colorScheme: "blue",
+ description: "REST API for accessing price feeds and updates",
+ },
+ };
+
+ // Generate product sections
+ const productSections: string[] = [];
+
+ for (const [productName, services] of Object.entries(productGroups)) {
+ const productTitle = formatProductTitle(productName);
+ const serviceCards: string[] = [];
+
+ for (const serviceName of services) {
+ const metadata = serviceMetadata[serviceName];
+ if (!metadata) continue;
+
+ const serviceTitle = formatServiceTitle(serviceName);
+ const serviceHref = `/api-reference/${productName}/${serviceName}`;
+
+ serviceCards.push(
+ ` }
+ colorScheme="${metadata.colorScheme}"
+ />`,
+ );
+ }
+
+ if (serviceCards.length > 0) {
+ productSections.push(`## ${productTitle}
+
+