diff --git a/fern/assets/styles.css b/fern/assets/styles.css
index e1a9d5349..dd5df1a73 100644
--- a/fern/assets/styles.css
+++ b/fern/assets/styles.css
@@ -67,12 +67,12 @@
margin: 0.5rem 1rem 0 1rem;
}
- .fern-product-selector-radio-group {
+ .fern-product-selector-radio-group {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
padding: 1.25rem;
- height: 164px;
+ height: 248px;
> a {
max-width: 320px;
@@ -104,6 +104,11 @@
grid-row: 2;
}
+ > a[href*="cli-api-reference"] {
+ grid-column: 1;
+ grid-row: 3;
+ }
+
> a[href*="openapi"]:before {
content: "Supported Specs";
font-weight: 500;
@@ -133,21 +138,24 @@
}
}
- > a[href*="cli-api-reference"] {
- grid-column: 3;
- grid-row: 3;
- transform: translateY(-64px);
-
- .fern-selection-item-icon {
- width: 1.5rem;
- height: 1.5rem;
- }
- }
-
+ /* > a[href*="asyncapi"]:before {
+ content: "Docs coming soon";
+ font-weight: 500;
+ position: absolute;
+ top: -1.75rem;
+ left: 0.5rem;
+ } */
+
> a[href*="asyncapi"] {
grid-column: 3;
grid-row: 3;
transform: translateY(-64px);
+ pointer-events: none;
+ cursor: not-allowed;
+
+ /* > div > div > div {
+ align-items: flex-start;
+ } */
.fern-selection-item-icon {
width: 1.5rem;
@@ -158,7 +166,13 @@
> a[href*="openrpc"] {
grid-column: 3;
grid-row: 4;
- transform: translateY(-64px);
+ transform: translateY(-88px);
+ pointer-events: none;
+ cursor: not-allowed;
+
+ /* > div > div > div {
+ align-items: flex-start;
+ } */
.fern-selection-item-icon {
width: 1.5rem;
@@ -169,8 +183,14 @@
> a[href*="grpc"] {
grid-column: 3;
grid-row: 5;
- transform: translateY(-64px);
+ transform: translateY(-88px);
+ pointer-events: none;
+ cursor: not-allowed;
+ /* > div > div > div {
+ align-items: flex-start;
+ } */
+
.fern-selection-item-icon {
width: 1.5rem;
height: 1.5rem;
@@ -209,18 +229,31 @@
content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/product-switcher-askfern-dark.png") !important;
}
+:is(.dark) .fern-product-selector-radio-group a[href*="cli-api-reference"] img {
+ content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/product-switcher-cliapi-dark.png") !important;
+}
+
:is(.dark) .fern-product-selector-radio-group a[href*="openapi-definition"] img {
content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/openapi-definition-dark.png") !important;
}
-:is(.dark) .fern-product-selector-radio-group a[href*="cli-api-reference"] img {
- content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/cli-api-reference-dark.png") !important;
-}
:is(.dark) .fern-product-selector-radio-group a[href*="fern-definition"] img {
content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/fern-definition-dark.png") !important;
}
+:is(.dark) .fern-product-selector-radio-group a[href*="asyncapi"] img {
+ content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/asyncapi-dark.png") !important;
+}
+
+:is(.dark) .fern-product-selector-radio-group a[href*="openrpc"] img {
+ content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/openrpc-dark.png") !important;
+}
+
+:is(.dark) .fern-product-selector-radio-group a[href*="grpc"] img {
+ content: url("https://fern-docs.s3.us-east-2.amazonaws.com/product-switcher/grpc-dark.png") !important;
+}
+
/*** END -- PRODUCT SELECTOR STYLING ***/
/*** START -- DOCS HOMEPAGE STYLING ***/
diff --git a/fern/docs.yml b/fern/docs.yml
index 8ae0985c8..6a155dbbb 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -63,28 +63,32 @@ products:
slug: fern-definition
- display-name: CLI & API Reference
+ subtitle: Manage and configure your Fern projects via CLI & APIs.
path: ./products/cli-api-reference/cli-api-reference.yml
icon: fa-regular fa-terminal
- image: ./images/product-switcher/cli-api-reference-light.png
+ image: ./images/product-switcher/product-switcher-cliapi-light.png
slug: cli-api-reference
+
+ - display-name: AsyncAPI
+ subtitle: Docs coming soon
+ path: ./products/asyncapi-def/asyncapi-def.yml
+ icon: fa-regular fa-bolt
+ image: ./images/product-switcher/asyncapi-light.png
+ slug: asyncapi
- # - display-name: AsyncAPI
- # path: ./products/asyncapi-def/asyncapi-def.yml
- # icon: fa-regular fa-bolt
- # image: ./images/product-switcher/asyncapi-def.png
- # slug: asyncapi
-
- # - display-name: OpenRPC
- # path: ./products/openrpc-def/openrpc-def.yml
- # icon: fa-regular fa-code
- # image: ./images/product-switcher/openrpc-def.png
- # slug: openrpc
+ - display-name: OpenRPC
+ subtitle: Docs coming soon
+ path: ./products/openrpc-def/openrpc-def.yml
+ icon: fa-regular fa-code
+ image: ./images/product-switcher/openrpc-light.png
+ slug: openrpc
- # - display-name: gRPC
- # path: ./products/grpc-def/grpc-def.yml
- # icon: fa-regular fa-plug
- # image: ./images/product-switcher/grpc-def.png
- # slug: grpc
+ - display-name: gRPC
+ subtitle: Docs coming soon
+ path: ./products/grpc-def/grpc-def.yml
+ icon: fa-regular fa-plug
+ image: ./images/product-switcher/grpc-light.png
+ slug: grpc
css: ./assets/styles.css
@@ -470,7 +474,7 @@ redirects:
destination: /learn/openapi-definition/extensions/:slug*
permanent: true
- source: /learn/api-definition/openapi/frameworks/:slug*
- destination: /learn/openapi-definition/server-frameworks/:slug*
+ destination: /learn/openapi-definition/frameworks/:slug*
permanent: true
- source: /learn/api-definition/openapi/:slug*
destination: /learn/openapi-definition/:slug*
diff --git a/fern/images/product-switcher/asyncapi-dark.png b/fern/images/product-switcher/asyncapi-dark.png
new file mode 100644
index 000000000..6c2db3684
Binary files /dev/null and b/fern/images/product-switcher/asyncapi-dark.png differ
diff --git a/fern/images/product-switcher/asyncapi-light.png b/fern/images/product-switcher/asyncapi-light.png
new file mode 100644
index 000000000..1ddf302b4
Binary files /dev/null and b/fern/images/product-switcher/asyncapi-light.png differ
diff --git a/fern/images/product-switcher/grpc-dark.png b/fern/images/product-switcher/grpc-dark.png
new file mode 100644
index 000000000..20cbc9250
Binary files /dev/null and b/fern/images/product-switcher/grpc-dark.png differ
diff --git a/fern/images/product-switcher/grpc-light.png b/fern/images/product-switcher/grpc-light.png
new file mode 100644
index 000000000..a39fe9f10
Binary files /dev/null and b/fern/images/product-switcher/grpc-light.png differ
diff --git a/fern/images/product-switcher/openrpc-dark.png b/fern/images/product-switcher/openrpc-dark.png
new file mode 100644
index 000000000..470d193a7
Binary files /dev/null and b/fern/images/product-switcher/openrpc-dark.png differ
diff --git a/fern/images/product-switcher/openrpc-light.png b/fern/images/product-switcher/openrpc-light.png
new file mode 100644
index 000000000..c6b1ea81d
Binary files /dev/null and b/fern/images/product-switcher/openrpc-light.png differ
diff --git a/fern/images/product-switcher/product-switcher-cliapi-dark.png b/fern/images/product-switcher/product-switcher-cliapi-dark.png
new file mode 100644
index 000000000..2b5aa4113
Binary files /dev/null and b/fern/images/product-switcher/product-switcher-cliapi-dark.png differ
diff --git a/fern/images/product-switcher/product-switcher-cliapi-light.png b/fern/images/product-switcher/product-switcher-cliapi-light.png
new file mode 100644
index 000000000..d7ddc8445
Binary files /dev/null and b/fern/images/product-switcher/product-switcher-cliapi-light.png differ
diff --git a/fern/products/asyncapi-def/asyncapi-def.yml b/fern/products/asyncapi-def/asyncapi-def.yml
index 07abf61a7..a59539c60 100644
--- a/fern/products/asyncapi-def/asyncapi-def.yml
+++ b/fern/products/asyncapi-def/asyncapi-def.yml
@@ -1,61 +1,39 @@
navigation:
- - section: Getting Started
+ - page: Overview
+ path: ./pages/overview.mdx
+ - page: Authentication
+ path: ./pages/auth.mdx
+ - page: Servers
+ path: ./pages/servers.mdx
+ - section: Channels
+ slug: channels
contents:
- - page: Overview
- path: ./pages/getting-started/overview.mdx
- - page: Get to OpenAPI
- path: ./pages/getting-started/get-to-openapi.mdx
- - page: Philosophy
- path: ./pages/getting-started/philosophy.mdx
- - page: Customer Showcase
- path: ./pages/getting-started/customer-showcase.mdx
- - section: Get to OpenAPI
- contents:
- - page: Handwrite it
- path: ./pages/getting-started/get-to-openapi/handwrite-it.mdx
- - section: Generate it
- contents:
- - page: FastAPI
- path: ./pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
- - page: NestJS
- path: ./pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
- - page: Swaggo
- path: ./pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
- - page: Request a new framework
- path: ./pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
- - page: Overlay customizations
- path: ./pages/getting-started/overlay-customizations.mdx
- - section: Security Schemes
+ - page: Publish/Subscribe Operations
+ path: ./pages/channels/pubsub.mdx
+ slug: pubsub
+ - page: Message Formats
+ path: ./pages/channels/messages.mdx
+ slug: messages
+ - page: Message Bindings
+ path: ./pages/channels/bindings.mdx
+ slug: bindings
+ - section: Extensions
+ slug: extensions
contents:
- - page: Overview
- path: ./pages/security-schemes/overview.mdx
- - page: Overriding the security scheme
- path: ./pages/security-schemes/overriding-security-scheme.mdx
- - page: Multiple security schemes
- path: ./pages/security-schemes/multiple-security-schemes.mdx
- - section: Servers
+ - page: Audiences
+ path: ./pages/extensions/audiences.mdx
+ - page: SDK Method Names
+ path: ./pages/extensions/method-names.mdx
+ slug: method-names
+ - page: Parameter Names
+ path: ./pages/extensions/parameter-names.mdx
+ - page: Other
+ path: ./pages/extensions/others.mdx
+ slug: others
+ - section: Workflow & Automation
+ skip-slug: true
contents:
- - page: Overview
- path: ./pages/servers/overview.mdx
- - page: Overriding the servers
- path: ./pages/servers/overriding-servers.mdx
- - page: Naming your servers
- path: ./pages/servers/naming-your-servers.mdx
- - page: Multiple server URLs
- path: ./pages/servers/multiple-server-urls.mdx
- - section: Schemas
- contents:
- - page: Objects
- path: ./pages/schemas/objects.mdx
- - page: AllOf
- path: ./pages/schemas/allof.mdx
- - page: OneOf
- path: ./pages/schemas/oneof.mdx
- - page: AnyOf
- path: ./pages/schemas/anyof.mdx
- - page: Enums
- path: ./pages/schemas/enums.mdx
- - page: Override the type
- path: ./pages/schemas/override-type.mdx
- - page: Inlined schemas
- path: ./pages/schemas/inlined-schemas.mdx
+ - page: Overlay Customizations
+ path: ./pages/overrides.mdx
+ - page: Sync your AsyncAPI Specification
+ path: ./pages/automation.mdx
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/auth.mdx b/fern/products/asyncapi-def/pages/auth.mdx
new file mode 100644
index 000000000..57f8bde2b
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/auth.mdx
@@ -0,0 +1,187 @@
+---
+title: Authentication
+subtitle: Model auth schemes such as bearer, basic, and api key for your event-driven APIs.
+---
+
+Configuring authentication schemes happens in the `components.securitySchemes` section of AsyncAPI.
+
+```yml title="asyncapi.yml" {2-3}
+components:
+ securitySchemes:
+ ...
+```
+
+
+To apply a security scheme across all operations, reference the `securityScheme` within the `security` section of your AsyncAPI Specification.
+
+```yml title="asyncapi.yml" {3, 5-6}
+components:
+ securitySchemes:
+ AuthScheme:
+ ...
+security:
+ - AuthScheme: []
+```
+
+
+## Bearer security scheme
+
+Start by defining a `bearer` security scheme in your `asyncapi.yml`:
+
+```yml title="asyncapi.yml" {3-5}
+components:
+ securitySchemes:
+ BearerAuth:
+ type: http
+ scheme: bearer
+```
+
+This will generate an SDK where the user would have to provide
+a mandatory argument called `token`.
+
+```ts index.ts
+const client = new Client({
+ token: "ey34..."
+})
+```
+
+If you want to control variable naming and the environment variable to scan,
+use the configuration below:
+
+```yml title="asyncapi.yml" {4-6}
+components:
+ securitySchemes:
+ BearerAuth:
+ type: http
+ scheme: bearer
+ x-fern-token:
+ name: authToken
+ env: AUTH_TOKEN
+```
+
+## API Key security scheme
+
+Start by defining an `apiKey` security scheme in your `asyncapi.yml`:
+
+```yml title="asyncapi.yml" {3-6}
+components:
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+```
+
+This will generate an SDK where the user would have to provide
+a mandatory argument called `apiKey`.
+
+```ts index.ts
+const client = new Client({
+ apiKey: "my-api-key"
+})
+```
+
+### Custom API Key variable name
+
+If you want to control variable naming and the environment variable to scan,
+use the configuration below:
+
+```yml title="asyncapi.yml" {5-7}
+components:
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ x-fern-api-key:
+ name: customApiKey
+ env: CUSTOM_API_KEY
+ in: header
+ name: X-API-Key
+```
+
+## Basic security scheme
+
+Start by defining a `basic` security scheme in your `asyncapi.yml`:
+
+```yml title="asyncapi.yml" {3-5}
+components:
+ securitySchemes:
+ BasicAuth:
+ type: http
+ scheme: basic
+```
+
+This will generate an SDK where the user would have to provide
+a `username` and `password`.
+
+```ts index.ts
+const client = new Client({
+ username: "john@example.com",
+ password: "password123"
+})
+```
+
+### Custom Basic Auth variable names
+
+If you want to control variable naming and the environment variables to scan,
+use the configuration below:
+
+```yml title="asyncapi.yml" {4-7}
+components:
+ securitySchemes:
+ BasicAuth:
+ type: http
+ scheme: basic
+ x-fern-username:
+ name: email
+ env: EMAIL
+ x-fern-password:
+ name: pass
+ env: PASSWORD
+```
+
+## OAuth2 security scheme
+
+OAuth2 authentication is supported for AsyncAPI specifications:
+
+```yml title="asyncapi.yml" {3-11}
+components:
+ securitySchemes:
+ OAuth2:
+ type: oauth2
+ flows:
+ authorizationCode:
+ authorizationUrl: https://example.com/oauth/authorize
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read: Read access to resources
+ write: Write access to resources
+```
+
+This will generate an SDK that supports OAuth2 flow for event-driven operations.
+
+## Multiple security schemes
+
+You can specify multiple security schemes and apply them to different operations:
+
+```yml title="asyncapi.yml" {2-12, 16-17}
+components:
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ BearerAuth:
+ type: http
+ scheme: bearer
+ OAuth2:
+ type: oauth2
+ flows: ...
+
+operations:
+ sendMessage:
+ security:
+ - ApiKeyAuth: []
+ - BearerAuth: []
+```
+
+This allows different operations to use different authentication methods as needed.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/automation.mdx b/fern/products/asyncapi-def/pages/automation.mdx
new file mode 100644
index 000000000..ffeb260cb
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/automation.mdx
@@ -0,0 +1,203 @@
+---
+title: Sync your AsyncAPI Specification
+subtitle: Automatically sync your AsyncAPI spec changes to keep SDKs and docs up to date
+---
+
+Keeping your AsyncAPI specifications in sync with your codebase is crucial for maintaining accurate SDKs and documentation. Fern provides several automation options to streamline this process.
+
+## GitHub Actions
+
+Use Fern's GitHub Action to automatically update SDKs and docs when your AsyncAPI spec changes:
+
+```yaml title=".github/workflows/fern.yml"
+name: Fern
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ fern-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Check AsyncAPI spec
+ uses: fern-api/action@v0
+ with:
+ command: check
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+
+ fern-generate:
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Generate SDKs and docs
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Webhook integration
+
+Set up webhooks to trigger SDK generation when your AsyncAPI spec is updated:
+
+```yaml title="generators.yml" {4-8}
+api:
+ specs:
+ - spec: asyncapi.yml
+ github:
+ repository: your-org/your-repo
+ webhooks:
+ - url: https://your-api.com/webhooks/fern
+ events: [generate]
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@your-org/sdk"
+```
+
+## Auto-sync from source
+
+Configure Fern to automatically pull your AsyncAPI specification from various sources:
+
+### From URL
+```yaml title="generators.yml" {3-4}
+api:
+ specs:
+ - spec: https://api.yourcompany.com/asyncapi.yml
+ auto-sync: true
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+### From Git repository
+```yaml title="generators.yml" {3-7}
+api:
+ specs:
+ - spec:
+ git:
+ repository: https://github.com/your-org/api-specs
+ path: asyncapi/api.yml
+ branch: main
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## CI/CD integration
+
+### CircleCI
+```yaml title=".circleci/config.yml" {15-23}
+version: 2.1
+
+orbs:
+ fern: fernapi/fern@1.0
+
+workflows:
+ version: 2
+ build-and-test:
+ jobs:
+ - build
+ - test:
+ requires:
+ - build
+ - fern/generate:
+ requires:
+ - test
+ filters:
+ branches:
+ only: main
+ context:
+ - fern-context
+```
+
+### GitLab CI
+```yaml title=".gitlab-ci.yml" {13-20}
+stages:
+ - build
+ - test
+ - generate
+
+variables:
+ FERN_TOKEN: $FERN_TOKEN
+
+build:
+ stage: build
+ script:
+ - echo "Building application..."
+
+generate-sdks:
+ stage: generate
+ image: fernapi/fern:latest
+ script:
+ - fern generate
+ only:
+ - main
+```
+
+## Scheduled updates
+
+Set up scheduled updates to ensure your SDKs stay current:
+
+```yaml title=".github/workflows/scheduled-update.yml"
+name: Scheduled AsyncAPI Update
+
+on:
+ schedule:
+ - cron: '0 2 * * 1' # Every Monday at 2 AM UTC
+ workflow_dispatch:
+
+jobs:
+ update-specs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Update AsyncAPI specs
+ run: |
+ curl -o fern/asyncapi/asyncapi.yml https://api.yourcompany.com/asyncapi.yml
+
+ - name: Generate with latest spec
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Monitoring changes
+
+Track changes to your AsyncAPI specification:
+
+```yaml title="generators.yml" {4-9}
+api:
+ specs:
+ - spec: asyncapi.yml
+ change-detection:
+ enabled: true
+ breaking-changes: error
+ notifications:
+ slack: ${{ secrets.SLACK_WEBHOOK }}
+ email: team@yourcompany.com
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+This ensures that any breaking changes to your AsyncAPI specification are detected and the appropriate team members are notified before the changes are propagated to your SDKs and documentation.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/channels/bindings.mdx b/fern/products/asyncapi-def/pages/channels/bindings.mdx
new file mode 100644
index 000000000..aac3ae4e2
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/channels/bindings.mdx
@@ -0,0 +1,214 @@
+---
+title: Message Bindings
+subtitle: Configure protocol-specific settings for WebSocket, MQTT, Kafka, and other protocols
+---
+
+Bindings in AsyncAPI allow you to specify protocol-specific information for servers, channels, operations, and messages. This enables you to provide detailed configuration for different messaging protocols.
+
+```yml title="asyncapi.yml" {6-12}
+channels:
+ user/notifications:
+ address: user/notifications
+ messages:
+ UserNotification:
+ $ref: '#/components/messages/UserNotification'
+ bindings:
+ ws:
+ method: GET
+ query:
+ type: object
+ properties:
+ token:
+ type: string
+ description: Authentication token
+```
+
+## WebSocket bindings
+
+Configure WebSocket-specific settings for real-time communication:
+
+```yml title="asyncapi.yml" {4-10}
+channels:
+ chat/room:
+ address: chat/room/{roomId}
+ bindings:
+ ws:
+ method: GET
+ headers:
+ type: object
+ properties:
+ Authorization:
+ type: string
+ query:
+ type: object
+ properties:
+ userId:
+ type: string
+ roomId:
+ type: string
+
+operations:
+ sendMessage:
+ action: send
+ channel:
+ $ref: '#/channels/chat~1room'
+ bindings:
+ ws:
+ type: request
+ method: message
+```
+
+## MQTT bindings
+
+Configure MQTT-specific settings for IoT and messaging applications:
+
+```yml title="asyncapi.yml" {4-8}
+channels:
+ device/telemetry:
+ address: device/{deviceId}/telemetry
+ bindings:
+ mqtt:
+ qos: 1
+ retain: true
+ messageExpiryInterval: 3600
+
+servers:
+ mqtt-broker:
+ host: mqtt.example.com
+ protocol: mqtt
+ bindings:
+ mqtt:
+ clientId: device-client
+ cleanSession: false
+ keepAlive: 60
+ will:
+ topic: device/status
+ payload: "offline"
+ qos: 1
+ retain: true
+```
+
+## Kafka bindings
+
+Configure Kafka-specific settings for high-throughput event streaming:
+
+```yml title="asyncapi.yml" {4-12}
+channels:
+ user.events:
+ address: user.events
+ bindings:
+ kafka:
+ topic: user.events
+ partitions: 10
+ replicas: 3
+ topicConfiguration:
+ cleanup.policy: ["delete"]
+ retention.ms: 604800000
+ segment.ms: 86400000
+
+operations:
+ publishUserEvent:
+ action: send
+ channel:
+ $ref: '#/channels/user.events'
+ bindings:
+ kafka:
+ groupId: user-event-processors
+ key:
+ type: string
+ description: User ID for partitioning
+```
+
+## Server bindings
+
+Configure protocol-specific server settings:
+
+```yml title="asyncapi.yml" {6-16}
+servers:
+ websocket-server:
+ host: ws.example.com
+ protocol: wss
+ description: WebSocket server with SSL
+ bindings:
+ ws:
+ headers:
+ type: object
+ properties:
+ X-API-Version:
+ type: string
+ const: "v1"
+
+ kafka-cluster:
+ host: kafka.example.com:9092
+ protocol: kafka
+ bindings:
+ kafka:
+ schemaRegistryUrl: https://schema-registry.example.com
+ schemaRegistryVendor: confluent
+```
+
+## Message bindings
+
+Configure how messages are handled by specific protocols:
+
+```yml title="asyncapi.yml" {8-15}
+components:
+ messages:
+ OrderEvent:
+ name: OrderEvent
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/Order'
+ bindings:
+ kafka:
+ key:
+ type: string
+ description: Order ID for consistent partitioning
+ headers:
+ type: object
+ properties:
+ eventType:
+ type: string
+ enum: [created, updated, cancelled]
+ mqtt:
+ qos: 2
+ retain: false
+```
+
+## HTTP bindings
+
+For HTTP-based protocols like Server-Sent Events:
+
+```yml title="asyncapi.yml" {4-8}
+channels:
+ events/stream:
+ address: /events/stream
+ bindings:
+ http:
+ type: response
+ method: GET
+ query:
+ type: object
+ properties:
+ lastEventId:
+ type: string
+ description: Last received event ID for resumption
+
+operations:
+ streamEvents:
+ action: send
+ channel:
+ $ref: '#/channels/events~1stream'
+ bindings:
+ http:
+ type: response
+ statusCode: 200
+ headers:
+ type: object
+ properties:
+ Content-Type:
+ type: string
+ const: text/event-stream
+```
+
+Bindings provide the flexibility to leverage protocol-specific features while maintaining a unified API specification across different transport mechanisms.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/channels/messages.mdx b/fern/products/asyncapi-def/pages/channels/messages.mdx
new file mode 100644
index 000000000..d0d182e17
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/channels/messages.mdx
@@ -0,0 +1,170 @@
+---
+title: Message Formats
+subtitle: Define message schemas, content types, and structure for event-driven communication
+---
+
+Messages in AsyncAPI are defined in the `components.messages` section and referenced from channels and operations. They define the structure and format of data exchanged through your event-driven API.
+
+```yml title="asyncapi.yml" {2-12}
+components:
+ messages:
+ UserEvent:
+ name: UserEvent
+ title: User Event Message
+ summary: Event triggered by user actions
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/UserEventPayload'
+ headers:
+ $ref: '#/components/schemas/MessageHeaders'
+```
+
+## Message payload
+
+The payload defines the structure of the message data:
+
+```yml title="asyncapi.yml" {4-6}
+components:
+ messages:
+ OrderCreated:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/Order'
+ schemas:
+ Order:
+ type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ description: Unique order identifier
+ customerId:
+ type: string
+ description: Customer who placed the order
+ items:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrderItem'
+ total:
+ type: number
+ format: decimal
+ description: Total order amount
+ createdAt:
+ type: string
+ format: date-time
+ description: When the order was created
+ required:
+ - id
+ - customerId
+ - items
+ - total
+```
+
+## Message headers
+
+You can define headers that are sent with your messages:
+
+```yml title="asyncapi.yml" {6-8}
+components:
+ messages:
+ NotificationEvent:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/Notification'
+ headers:
+ $ref: '#/components/schemas/NotificationHeaders'
+ schemas:
+ NotificationHeaders:
+ type: object
+ properties:
+ messageId:
+ type: string
+ description: Unique message identifier
+ timestamp:
+ type: string
+ format: date-time
+ description: Message timestamp
+ source:
+ type: string
+ description: Source service that generated the message
+ priority:
+ type: string
+ enum: [low, medium, high, urgent]
+ description: Message priority level
+```
+
+## Content types
+
+AsyncAPI supports various content types for messages:
+
+```yml title="asyncapi.yml" {4,10,16}
+components:
+ messages:
+ JsonMessage:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/JsonPayload'
+
+ BinaryMessage:
+ contentType: application/octet-stream
+ payload:
+ type: string
+ format: binary
+
+ TextMessage:
+ contentType: text/plain
+ payload:
+ type: string
+```
+
+## Message examples
+
+Provide concrete examples to help developers understand your message format:
+
+```yml title="asyncapi.yml" {8-18}
+components:
+ messages:
+ UserSignup:
+ name: UserSignup
+ title: User Signup Event
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/User'
+ examples:
+ - name: StandardSignup
+ summary: Regular user signup
+ payload:
+ id: "123e4567-e89b-12d3-a456-426614174000"
+ email: "john@example.com"
+ name: "John Doe"
+ signupSource: "web"
+ createdAt: "2024-01-15T10:30:00Z"
+```
+
+## Message traits
+
+Use traits to share common message properties across multiple messages:
+
+```yml title="asyncapi.yml" {2-12, 15-19}
+components:
+ messageTraits:
+ commonHeaders:
+ headers:
+ type: object
+ properties:
+ messageId:
+ type: string
+ timestamp:
+ type: string
+ format: date-time
+
+ messages:
+ UserEvent:
+ traits:
+ - $ref: '#/components/messageTraits/commonHeaders'
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/UserEventPayload'
+```
+
+This approach helps maintain consistency across your message definitions while reducing duplication.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/channels/pubsub.mdx b/fern/products/asyncapi-def/pages/channels/pubsub.mdx
new file mode 100644
index 000000000..cd51ae39c
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/channels/pubsub.mdx
@@ -0,0 +1,105 @@
+---
+title: Publish/Subscribe Operations
+subtitle: Document event-driven APIs with publish and subscribe operations
+---
+
+Operations in AsyncAPI are defined underneath the `operations` key, with channels defined under the `channels` key. Below is an example of defining publish and subscribe operations:
+
+```yml title="asyncapi.yml" maxLines=0 {2-22}
+channels:
+ user/notifications:
+ address: user/notifications
+ messages:
+ UserNotification:
+ $ref: '#/components/messages/UserNotification'
+
+operations:
+ onUserNotification:
+ action: receive
+ channel:
+ $ref: '#/channels/user~1notifications'
+ summary: Receive user notifications
+ description: Subscribe to user notification events
+ sendUserNotification:
+ action: send
+ channel:
+ $ref: '#/channels/user~1notifications'
+ summary: Send user notification
+ description: Publish a user notification event
+```
+
+## Message examples
+
+You can provide examples of messages by using the `examples` key in your message definitions:
+
+```yaml title="asyncapi.yml" {8-15}
+components:
+ messages:
+ UserNotification:
+ name: UserNotification
+ title: User Notification
+ summary: Notification sent to a user
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/Notification'
+ examples:
+ - name: EmailNotification
+ summary: Example email notification
+ payload:
+ userId: "123e4567-e89b-12d3-a456-426614174000"
+ type: "email"
+ message: "Welcome to our service!"
+ priority: "medium"
+```
+
+## Publish operations
+
+Publish operations represent messages that your service sends to a channel:
+
+```yml title="asyncapi.yml" {2-8}
+operations:
+ publishOrderStatus:
+ action: send
+ channel:
+ $ref: '#/channels/order~1status'
+ summary: Publish order status update
+ description: Send order status updates to subscribers
+ message:
+ $ref: '#/components/messages/OrderStatus'
+```
+
+## Subscribe operations
+
+Subscribe operations represent messages that your service receives from a channel:
+
+```yml title="asyncapi.yml" {2-8}
+operations:
+ subscribeToUserSignups:
+ action: receive
+ channel:
+ $ref: '#/channels/user~1signup'
+ summary: Subscribe to user signups
+ description: Receive notifications when users sign up
+ message:
+ $ref: '#/components/messages/UserSignup'
+```
+
+## Bi-directional communication
+
+You can define both publish and subscribe operations for the same channel to enable bi-directional communication:
+
+```yml title="asyncapi.yml" {2-15}
+operations:
+ sendChatMessage:
+ action: send
+ channel:
+ $ref: '#/channels/chat~1room'
+ summary: Send chat message
+ description: Send a message to a chat room
+ receiveChatMessage:
+ action: receive
+ channel:
+ $ref: '#/channels/chat~1room'
+ summary: Receive chat message
+ description: Receive messages from a chat room
+```
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/extensions/audiences.mdx b/fern/products/asyncapi-def/pages/extensions/audiences.mdx
new file mode 100644
index 000000000..bc1118a80
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/extensions/audiences.mdx
@@ -0,0 +1,157 @@
+---
+title: Use audiences to filter your API
+subtitle: Use `x-fern-audiences` to filter to relevant operations, channels and message schemas
+---
+
+Audiences are a useful tool for segmenting your event-driven API for different consumers. Common examples of audiences include `public`
+and `beta`.
+
+
+ Remember to filter your SDKs and Docs after specifying audiences. If **no audiences** are specified,
+ nothing will be filtered.
+
+
+
+
+The following example configures the SDK to filter to the `public` audience:
+
+```yaml title="generators.yml" {3-4}
+groups:
+ sdks:
+ audiences:
+ - public
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+
+
+The following example configures the docs to filter to the `public` audience:
+
+```yaml title="docs.yml" {3-4}
+navigation:
+ - api: API Reference
+ audiences:
+ - public
+```
+
+
+
+
+
+## Filter operations
+
+Add `x-fern-audiences` to operations to control which operations are included for specific audiences:
+
+```yaml title="asyncapi.yml" {5-6, 12-13}
+operations:
+ sendPublicNotification:
+ action: send
+ channel:
+ $ref: '#/channels/public~1notifications'
+ x-fern-audiences:
+ - public
+ sendBetaAlert:
+ action: send
+ channel:
+ $ref: '#/channels/beta~1alerts'
+ x-fern-audiences:
+ - beta
+```
+
+## Filter channels
+
+You can also filter entire channels by audience:
+
+```yaml title="asyncapi.yml" {6-7, 13-14}
+channels:
+ public/events:
+ address: public/events
+ messages:
+ PublicEvent:
+ $ref: '#/components/messages/PublicEvent'
+ x-fern-audiences:
+ - public
+ internal/events:
+ address: internal/events
+ messages:
+ InternalEvent:
+ $ref: '#/components/messages/InternalEvent'
+ x-fern-audiences:
+ - internal
+```
+
+## Filter message schemas
+
+Filter specific message schemas to different audiences:
+
+```yaml title="asyncapi.yml" {5-6, 13-14}
+components:
+ messages:
+ PublicUserEvent:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/PublicUser'
+ x-fern-audiences:
+ - public
+ AdminUserEvent:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/AdminUser'
+ x-fern-audiences:
+ - admin
+ schemas:
+ PublicUser:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ email:
+ type: string
+ x-fern-audiences:
+ - public
+ AdminUser:
+ allOf:
+ - $ref: '#/components/schemas/PublicUser'
+ - type: object
+ properties:
+ role:
+ type: string
+ permissions:
+ type: array
+ items:
+ type: string
+ x-fern-audiences:
+ - admin
+```
+
+## Filter schema properties
+
+You can filter individual properties within schemas:
+
+```yaml title="asyncapi.yml" {9-10, 13-14}
+components:
+ schemas:
+ UserEvent:
+ type: object
+ properties:
+ id:
+ type: string
+ email:
+ type: string
+ x-fern-audiences:
+ - internal
+ publicName:
+ type: string
+ x-fern-audiences:
+ - public
+ internalNotes:
+ type: string
+ x-fern-audiences:
+ - internal
+```
+
+This allows you to have different views of the same event schema for different audiences, showing only the relevant information to each consumer.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/extensions/method-names.mdx b/fern/products/asyncapi-def/pages/extensions/method-names.mdx
new file mode 100644
index 000000000..ec4234035
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/extensions/method-names.mdx
@@ -0,0 +1,94 @@
+---
+title: SDK Method Names
+subtitle: Use `x-fern-sdk-method-name` to customize SDK method names for operations
+---
+
+By default, Fern generates SDK method names based on your operation names in AsyncAPI. You can override this behavior using the `x-fern-sdk-method-name` extension.
+
+## Customize operation method names
+
+Use `x-fern-sdk-method-name` to specify custom method names for your operations:
+
+```yaml title="asyncapi.yml" {6-7, 13-14}
+operations:
+ subscribeToUserEvents:
+ action: receive
+ channel:
+ $ref: '#/channels/user~1events'
+ x-fern-sdk-method-name: onUserEvent
+ summary: Subscribe to user events
+ publishOrderUpdate:
+ action: send
+ channel:
+ $ref: '#/channels/order~1updates'
+ x-fern-sdk-method-name: sendOrderUpdate
+ summary: Publish order update
+```
+
+This will generate SDK methods like:
+
+```typescript
+// Instead of client.subscribeToUserEvents()
+client.onUserEvent((event) => {
+ // Handle user event
+});
+
+// Instead of client.publishOrderUpdate()
+client.sendOrderUpdate(orderData);
+```
+
+## Method naming conventions
+
+Follow these conventions when naming SDK methods:
+
+### Subscribe operations
+Use descriptive names that indicate the action:
+
+```yaml title="asyncapi.yml" {4-5}
+operations:
+ subscribeToNotifications:
+ action: receive
+ x-fern-sdk-method-name: onNotification
+ # Generates: client.onNotification()
+
+ subscribeToOrderUpdates:
+ action: receive
+ x-fern-sdk-method-name: watchOrders
+ # Generates: client.watchOrders()
+```
+
+### Publish operations
+Use action-oriented names:
+
+```yaml title="asyncapi.yml" {4-5}
+operations:
+ publishUserSignup:
+ action: send
+ x-fern-sdk-method-name: createUser
+ # Generates: client.createUser()
+
+ publishNotification:
+ action: send
+ x-fern-sdk-method-name: notify
+ # Generates: client.notify()
+```
+
+## Language-specific method names
+
+You can specify different method names for different programming languages:
+
+```yaml title="asyncapi.yml" {6-11}
+operations:
+ subscribeToEvents:
+ action: receive
+ channel:
+ $ref: '#/channels/events'
+ x-fern-sdk-method-name:
+ python: on_event
+ typescript: onEvent
+ go: OnEvent
+ java: onEvent
+ csharp: OnEvent
+```
+
+This ensures method names follow the conventions of each target language.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/extensions/others.mdx b/fern/products/asyncapi-def/pages/extensions/others.mdx
new file mode 100644
index 000000000..cbc7d15af
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/extensions/others.mdx
@@ -0,0 +1,160 @@
+---
+title: Other Extensions
+subtitle: Additional Fern extensions for AsyncAPI specifications
+---
+
+Fern supports various extensions to enhance your AsyncAPI specifications and improve the generated SDKs and documentation.
+
+## `x-fern-ignore`
+
+Use `x-fern-ignore` to exclude specific operations, channels, or schemas from SDK generation:
+
+```yaml title="asyncapi.yml" {6-7, 13-14}
+operations:
+ debugOperation:
+ action: send
+ channel:
+ $ref: '#/channels/debug'
+ x-fern-ignore: true
+ summary: Debug operation (internal only)
+
+channels:
+ internal/debug:
+ address: internal/debug
+ x-fern-ignore: true
+ messages:
+ DebugMessage:
+ $ref: '#/components/messages/DebugMessage'
+```
+
+## `x-fern-examples`
+
+Provide additional examples for better SDK documentation:
+
+```yaml title="asyncapi.yml" {8-20}
+components:
+ messages:
+ UserEvent:
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/User'
+ x-fern-examples:
+ - name: NewUserSignup
+ summary: Example of a new user signup event
+ payload:
+ id: "user_123"
+ email: "john@example.com"
+ name: "John Doe"
+ status: "active"
+ - name: UserDeactivation
+ summary: Example of user deactivation event
+ payload:
+ id: "user_456"
+ email: "jane@example.com"
+ name: "Jane Smith"
+ status: "inactive"
+```
+
+## `x-fern-pagination`
+
+Configure pagination for operations that return multiple results:
+
+```yaml title="asyncapi.yml" {6-11}
+operations:
+ listUserEvents:
+ action: receive
+ channel:
+ $ref: '#/channels/user~1events'
+ x-fern-pagination:
+ cursor: next_cursor
+ results: events
+ next_cursor: pagination.next_cursor
+ has_next_page: pagination.has_next_page
+```
+
+## `x-fern-retry`
+
+Configure retry behavior for operations:
+
+```yaml title="asyncapi.yml" {6-10}
+operations:
+ sendCriticalAlert:
+ action: send
+ channel:
+ $ref: '#/channels/alerts'
+ x-fern-retry:
+ max_attempts: 3
+ exponential_backoff: true
+ initial_delay: 1000
+ max_delay: 30000
+```
+
+## `x-fern-streaming`
+
+Mark operations as streaming for appropriate SDK generation:
+
+```yaml title="asyncapi.yml" {6-9}
+operations:
+ streamEvents:
+ action: receive
+ channel:
+ $ref: '#/channels/event~1stream'
+ x-fern-streaming:
+ type: server_sent_events
+ termination: client_closes
+```
+
+## `x-fern-error-handling`
+
+Configure error handling for operations:
+
+```yaml title="asyncapi.yml" {6-16}
+operations:
+ sendMessage:
+ action: send
+ channel:
+ $ref: '#/channels/messages'
+ x-fern-error-handling:
+ error_schema:
+ $ref: '#/components/schemas/MessageError'
+ error_status_codes:
+ - 400
+ - 429
+ - 500
+ retry_on_errors:
+ - 429
+ - 500
+```
+
+## `x-fern-server-name`
+
+Specify custom names for servers:
+
+```yaml title="asyncapi.yml" {4-5}
+servers:
+ production:
+ host: api.yourcompany.com
+ x-fern-server-name: Production
+ protocol: wss
+ staging:
+ host: staging.api.yourcompany.com
+ x-fern-server-name: Staging
+ protocol: wss
+```
+
+## `x-fern-availability`
+
+Mark features as available in specific SDK versions:
+
+```yaml title="asyncapi.yml" {6-8}
+operations:
+ newFeature:
+ action: send
+ channel:
+ $ref: '#/channels/new~1feature'
+ x-fern-availability:
+ status: beta
+ message: "This feature is in beta and may change"
+```
+
+These extensions help you create more robust and user-friendly SDKs while maintaining full control over the generated code structure and behavior.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/extensions/parameter-names.mdx b/fern/products/asyncapi-def/pages/extensions/parameter-names.mdx
new file mode 100644
index 000000000..8e5e47a3b
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/extensions/parameter-names.mdx
@@ -0,0 +1,79 @@
+---
+title: Parameter Names
+subtitle: Use `x-fern-parameter-name` to customize SDK parameter names for message properties
+---
+
+By default, Fern uses the property names from your AsyncAPI message schemas as SDK parameter names. You can customize these using the `x-fern-parameter-name` extension.
+
+## Customize message property names
+
+Use `x-fern-parameter-name` to specify custom parameter names for message properties:
+
+```yaml title="asyncapi.yml" {10-11, 14-15}
+components:
+ schemas:
+ UserEvent:
+ type: object
+ properties:
+ user_id:
+ type: string
+ description: Unique user identifier
+ x-fern-parameter-name: userId
+ created_at:
+ type: string
+ format: date-time
+ x-fern-parameter-name: createdAt
+ notification_type:
+ type: string
+ enum: [email, sms, push]
+ x-fern-parameter-name: notificationType
+```
+
+This generates SDK methods with cleaner parameter names:
+
+```typescript
+// Instead of send({user_id: "123", created_at: new Date(), notification_type: "email"})
+client.sendNotification({
+ userId: "123",
+ createdAt: new Date(),
+ notificationType: "email"
+});
+```
+
+## Language-specific parameter names
+
+You can specify different parameter names for different programming languages:
+
+```yaml title="asyncapi.yml" {8-13}
+components:
+ schemas:
+ OrderEvent:
+ type: object
+ properties:
+ order_id:
+ type: string
+ x-fern-parameter-name:
+ python: order_id
+ typescript: orderId
+ go: OrderID
+ java: orderId
+ csharp: OrderId
+```
+
+## Channel parameter names
+
+Customize parameter names for channel address variables:
+
+```yaml title="asyncapi.yml" {4-7}
+channels:
+ user/{user_id}/notifications:
+ address: user/{user_id}/notifications
+ parameters:
+ user_id:
+ description: User identifier
+ x-fern-parameter-name: userId
+ schema:
+ type: string
+```
+
+This ensures consistent naming conventions across your SDK while maintaining compatibility with your existing message formats.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/customer-showcase.mdx b/fern/products/asyncapi-def/pages/getting-started/customer-showcase.mdx
deleted file mode 100644
index be7c06684..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/customer-showcase.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Customer Showcase
----
-
-Examples and case studies of customers using AsyncAPI definitions successfully.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi.mdx
deleted file mode 100644
index faf5d5d7a..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Get to OpenAPI
----
-
-How to transition from AsyncAPI to OpenAPI or integrate both specifications.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
deleted file mode 100644
index 6ba0217d6..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: FastAPI
----
-
-# Generate OpenAPI from FastAPI
-
-Instructions for generating an OpenAPI definition from a FastAPI project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
deleted file mode 100644
index 838467f79..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: NestJS
----
-
-# Generate OpenAPI from NestJS
-
-Instructions for generating an OpenAPI definition from a NestJS project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
deleted file mode 100644
index 0ba737a50..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Request New Framework
----
-
-# Request a New Framework
-
-Don't see your framework? Let us know which framework you'd like to see supported for OpenAPI generation.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
deleted file mode 100644
index da566a29e..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Swaggo
----
-
-# Generate OpenAPI from Swaggo
-
-Instructions for generating an OpenAPI definition from a Swaggo (Go) project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
diff --git a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/handwrite-it.mdx b/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
deleted file mode 100644
index 28571f07c..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Handwrite It
----
-
-# Handwrite Your OpenAPI Definition
-
-Step-by-step guide to manually writing your OpenAPI definition from scratch.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/overlay-customizations.mdx b/fern/products/asyncapi-def/pages/getting-started/overlay-customizations.mdx
deleted file mode 100644
index 00fd99ab9..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/overlay-customizations.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overlay Customizations
----
-
-Learn how to customize your OpenAPI overlays to fit your unique requirements.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
diff --git a/fern/products/asyncapi-def/pages/getting-started/overview.mdx b/fern/products/asyncapi-def/pages/getting-started/overview.mdx
deleted file mode 100644
index d3b20d894..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overview
----
-
-This is the overview page for Getting Started with your OpenAPI definition. Here you will find a high-level introduction and guidance on how to begin.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/getting-started/philosophy.mdx b/fern/products/asyncapi-def/pages/getting-started/philosophy.mdx
deleted file mode 100644
index 7b9f1f487..000000000
--- a/fern/products/asyncapi-def/pages/getting-started/philosophy.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Philosophy
----
-
-The philosophy and principles behind AsyncAPI definitions and how they enhance API development.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/overrides.mdx b/fern/products/asyncapi-def/pages/overrides.mdx
new file mode 100644
index 000000000..0e76d5f18
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/overrides.mdx
@@ -0,0 +1,168 @@
+---
+title: Overlay Customizations
+subtitle: Use overlay files to modify your AsyncAPI specification without editing the original
+---
+
+Overlays allow you to modify your AsyncAPI specification without directly editing the original file. This is useful for:
+- Adding Fern-specific extensions
+- Customizing documentation
+- Adding examples and descriptions
+- Overriding specific properties
+
+## Configure overlays
+
+To use overlays, add them to your `generators.yml` file:
+
+```yaml title="generators.yml" {3-5}
+api:
+ specs:
+ - spec: asyncapi.yml
+ overlays:
+ - overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Overlay file structure
+
+Overlay files follow the [OpenAPI Overlay Specification](https://spec.openapis.org/overlay/v1.0.0) format:
+
+```yaml title="overlay.yml"
+overlay: 1.0.0
+info:
+ title: AsyncAPI Fern Extensions
+ version: 1.0.0
+actions:
+ - target: $.operations.sendNotification
+ update:
+ x-fern-sdk-method-name: notify
+ summary: Send a notification to a user
+ - target: $.channels['user/events']
+ update:
+ description: Channel for user-related events
+ x-fern-audiences:
+ - public
+```
+
+## Add method names
+
+Override SDK method names for better developer experience:
+
+```yaml title="overlay.yml" {7-9}
+overlay: 1.0.0
+info:
+ title: SDK Method Names
+ version: 1.0.0
+actions:
+ - target: $.operations.subscribeToUserEvents
+ update:
+ x-fern-sdk-method-name: onUserEvent
+ summary: Subscribe to user events with callback
+ - target: $.operations.publishOrderUpdate
+ update:
+ x-fern-sdk-method-name: updateOrder
+ summary: Publish order status update
+```
+
+## Add examples
+
+Enhance your specification with examples:
+
+```yaml title="overlay.yml" {7-17}
+overlay: 1.0.0
+info:
+ title: Message Examples
+ version: 1.0.0
+actions:
+ - target: $.components.messages.UserSignup
+ update:
+ examples:
+ - name: StandardSignup
+ summary: Regular user signup
+ payload:
+ id: "user_123"
+ email: "john@example.com"
+ name: "John Doe"
+ signupSource: "web"
+ timestamp: "2024-01-15T10:30:00Z"
+```
+
+## Filter with audiences
+
+Add audience filtering to operations and channels:
+
+```yaml title="overlay.yml" {7-9, 12-14}
+overlay: 1.0.0
+info:
+ title: Audience Filtering
+ version: 1.0.0
+actions:
+ - target: $.operations.adminAlert
+ update:
+ x-fern-audiences:
+ - admin
+ - target: $.channels['internal/debug']
+ update:
+ x-fern-audiences:
+ - internal
+```
+
+## Add documentation
+
+Enhance descriptions and documentation:
+
+```yaml title="overlay.yml" {7-11}
+overlay: 1.0.0
+info:
+ title: Enhanced Documentation
+ version: 1.0.0
+actions:
+ - target: $.operations.sendMessage
+ update:
+ description: |
+ Send a message to the specified channel. This operation supports
+ real-time delivery with automatic retry on failure. Messages are
+ guaranteed to be delivered at least once.
+ summary: Send message with delivery guarantees
+```
+
+## Server configurations
+
+Add server-specific configurations:
+
+```yaml title="overlay.yml" {7-11}
+overlay: 1.0.0
+info:
+ title: Server Extensions
+ version: 1.0.0
+actions:
+ - target: $.servers.production
+ update:
+ x-fern-server-name: Production
+ description: Production environment with high availability
+ bindings:
+ ws:
+ headers:
+ type: object
+ properties:
+ X-API-Version:
+ type: string
+ const: "v1"
+```
+
+## Multiple overlays
+
+You can apply multiple overlay files in sequence:
+
+```yaml title="generators.yml" {4-7}
+api:
+ specs:
+ - spec: asyncapi.yml
+ overlays:
+ - base-overlay.yml
+ - sdk-overlay.yml
+ - docs-overlay.yml
+```
+
+Overlays are applied in order, allowing you to build up customizations incrementally while keeping your original AsyncAPI specification clean and focused.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/overview.mdx b/fern/products/asyncapi-def/pages/overview.mdx
new file mode 100644
index 000000000..e11337086
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/overview.mdx
@@ -0,0 +1,130 @@
+---
+title: What is an AsyncAPI Specification?
+subtitle: AsyncAPI is a standard for documenting event-driven APIs
+---
+
+The AsyncAPI Specification is a framework used by developers to document event-driven APIs. The specification
+is written in JSON or YAML and contains all of your channels, messages, schemas, and authentication schemes.
+Fern is compatible with AsyncAPI specification [v2.6.0](https://www.asyncapi.com/docs/reference/specification/v2.6.0) and [v3.0.0](https://www.asyncapi.com/docs/reference/specification/v3.0.0).
+
+ Considering options to generate an AsyncAPI spec? Get live support [here](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)
+
+Below is an example of an AsyncAPI file:
+
+```yaml asyncapi.yml
+asyncapi: 3.0.0
+info:
+ title: User Notification Service
+ version: 1.0.0
+ description: |
+ Service that handles user notifications through various channels
+ including email, SMS, and push notifications.
+channels:
+ user/signup:
+ address: user/signup
+ messages:
+ UserSignedUp:
+ $ref: '#/components/messages/UserSignedUp'
+ notification/send:
+ address: notification/send
+ messages:
+ SendNotification:
+ $ref: '#/components/messages/SendNotification'
+operations:
+ onUserSignup:
+ action: receive
+ channel:
+ $ref: '#/channels/user~1signup'
+ summary: User signup event
+ description: Triggered when a user signs up for the service
+ sendNotification:
+ action: send
+ channel:
+ $ref: '#/channels/notification~1send'
+ summary: Send notification
+ description: Send a notification to a user
+components:
+ messages:
+ UserSignedUp:
+ name: UserSignedUp
+ title: User Signed Up
+ summary: User has signed up for the service
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/User'
+ SendNotification:
+ name: SendNotification
+ title: Send Notification
+ summary: Send a notification to a user
+ contentType: application/json
+ payload:
+ $ref: '#/components/schemas/Notification'
+ schemas:
+ User:
+ type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ description: Unique identifier for the user
+ email:
+ type: string
+ format: email
+ description: User's email address
+ name:
+ type: string
+ description: User's full name
+ createdAt:
+ type: string
+ format: date-time
+ description: When the user was created
+ required:
+ - id
+ - email
+ - name
+ Notification:
+ type: object
+ properties:
+ userId:
+ type: string
+ format: uuid
+ description: ID of the user to notify
+ type:
+ type: string
+ enum: [email, sms, push]
+ description: Type of notification to send
+ message:
+ type: string
+ description: Message content
+ priority:
+ type: string
+ enum: [low, medium, high, urgent]
+ default: medium
+ description: Priority level of the notification
+ required:
+ - userId
+ - type
+ - message
+```
+
+## Setup your fern folder
+
+Start by initializing your fern folder with an AsyncAPI spec
+
+
+```sh file
+fern init --asyncapi ./path/to/asyncapi
+```
+```sh url
+fern init --asyncapi https://host/path/to/asyncapi
+```
+
+
+This will initialize a directory like the following
+```
+fern/
+ ├─ fern.config.json
+ ├─ generators.yml
+ └─ asyncapi/
+ ├─ asyncapi.yml
+```
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/allof.mdx b/fern/products/asyncapi-def/pages/schemas/allof.mdx
deleted file mode 100644
index 16d657582..000000000
--- a/fern/products/asyncapi-def/pages/schemas/allof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: All Of
----
-
-How to use the `allOf` keyword to compose schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/anyof.mdx b/fern/products/asyncapi-def/pages/schemas/anyof.mdx
deleted file mode 100644
index 91fcdeab8..000000000
--- a/fern/products/asyncapi-def/pages/schemas/anyof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Any Of
----
-
-How to use the `anyOf` keyword to allow multiple possible schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/enums.mdx b/fern/products/asyncapi-def/pages/schemas/enums.mdx
deleted file mode 100644
index c9c85709a..000000000
--- a/fern/products/asyncapi-def/pages/schemas/enums.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Enums
----
-
-How to use enum types to define a fixed set of values in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/inlined-schemas.mdx b/fern/products/asyncapi-def/pages/schemas/inlined-schemas.mdx
deleted file mode 100644
index 9906fcf9d..000000000
--- a/fern/products/asyncapi-def/pages/schemas/inlined-schemas.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Inlined Schemas
----
-
-How to define schemas inline within your OpenAPI specification.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/objects.mdx b/fern/products/asyncapi-def/pages/schemas/objects.mdx
deleted file mode 100644
index 393909d69..000000000
--- a/fern/products/asyncapi-def/pages/schemas/objects.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Objects
----
-
-How to define object schemas with properties in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/oneof.mdx b/fern/products/asyncapi-def/pages/schemas/oneof.mdx
deleted file mode 100644
index 134e74233..000000000
--- a/fern/products/asyncapi-def/pages/schemas/oneof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: One Of
----
-
-How to use the `oneOf` keyword to define alternative schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/schemas/override-type.mdx b/fern/products/asyncapi-def/pages/schemas/override-type.mdx
deleted file mode 100644
index 109113ac7..000000000
--- a/fern/products/asyncapi-def/pages/schemas/override-type.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Override Type
----
-
-How to override the default type inference for schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/security-schemes/multiple-security-schemes.mdx b/fern/products/asyncapi-def/pages/security-schemes/multiple-security-schemes.mdx
deleted file mode 100644
index 57df008d3..000000000
--- a/fern/products/asyncapi-def/pages/security-schemes/multiple-security-schemes.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Security Schemes
----
-
-Learn how to define and use multiple security schemes in your OpenAPI definition.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/security-schemes/overriding-security-scheme.mdx b/fern/products/asyncapi-def/pages/security-schemes/overriding-security-scheme.mdx
deleted file mode 100644
index a3c7c6bd7..000000000
--- a/fern/products/asyncapi-def/pages/security-schemes/overriding-security-scheme.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Security Scheme
----
-
-How to override security schemes in specific operations or contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/security-schemes/overview.mdx b/fern/products/asyncapi-def/pages/security-schemes/overview.mdx
deleted file mode 100644
index 58347b318..000000000
--- a/fern/products/asyncapi-def/pages/security-schemes/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Security Schemes Overview
----
-
-Overview of security schemes and authentication methods in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/servers.mdx b/fern/products/asyncapi-def/pages/servers.mdx
new file mode 100644
index 000000000..a3a0554ec
--- /dev/null
+++ b/fern/products/asyncapi-def/pages/servers.mdx
@@ -0,0 +1,116 @@
+---
+title: Servers
+description: Configure server URLs and protocols to help users connect to your event-driven API.
+subtitle: Define server URLs and protocols to help users connect to your event-driven API.
+---
+
+AsyncAPI allows you to specify one or more server configurations under the `servers` key.
+
+```yml asyncapi.yml
+
+servers:
+ production:
+ host: api.yourcompany.com
+ protocol: wss
+ description: Production WebSocket server
+ staging:
+ host: staging.api.yourcompany.com
+ protocol: wss
+ description: Staging WebSocket server
+```
+
+Specifying servers is valuable for both SDKs and Docs:
+- For SDKs, your users won't need to manually specify the server URL at client instantiation
+- For Docs, your API playground will automatically connect to the correct server
+
+## Protocol support
+
+AsyncAPI supports various protocols for event-driven communication:
+
+```yml asyncapi.yml {4,8,12}
+servers:
+ websocket-server:
+ host: ws.api.yourcompany.com
+ protocol: ws
+ description: WebSocket server for real-time communication
+ mqtt-server:
+ host: mqtt.yourcompany.com
+ protocol: mqtt
+ description: MQTT broker for IoT devices
+ kafka-server:
+ host: kafka.yourcompany.com
+ protocol: kafka
+ description: Kafka cluster for event streaming
+```
+
+## Naming your servers
+
+We recommend giving your servers descriptive names to make it clear what each server is for:
+
+```yml asyncapi.yml
+servers:
+ production:
+ host: api.yourcompany.com
+ protocol: wss
+ description: Production WebSocket server
+ staging:
+ host: staging.api.yourcompany.com
+ protocol: wss
+ description: Staging environment for testing
+ development:
+ host: localhost:8080
+ protocol: ws
+ description: Local development server
+```
+
+## Server variables
+
+You can use variables in your server configurations to make them more flexible:
+
+```yml asyncapi.yml {3-10}
+servers:
+ production:
+ host: '{environment}.api.yourcompany.com'
+ protocol: wss
+ variables:
+ environment:
+ default: prod
+ enum:
+ - prod
+ - staging
+ description: Environment name
+```
+
+## Multiple protocols for different channels
+
+If you have different channels that use different protocols, you can specify this in your server configuration:
+
+```yml asyncapi.yml {2-14, 17-21}
+servers:
+ websocket-server:
+ host: ws.api.yourcompany.com
+ protocol: wss
+ description: WebSocket server for real-time notifications
+ mqtt-server:
+ host: mqtt.api.yourcompany.com
+ protocol: mqtt
+ description: MQTT broker for IoT device communication
+ kafka-server:
+ host: kafka.api.yourcompany.com
+ protocol: kafka
+ description: Kafka for high-throughput event streaming
+
+channels:
+ user/notifications:
+ servers:
+ - websocket-server
+ address: user/notifications
+ description: Real-time user notifications via WebSocket
+ device/telemetry:
+ servers:
+ - mqtt-server
+ address: device/telemetry
+ description: IoT device telemetry via MQTT
+```
+
+This allows different channels to use the most appropriate protocol for their use case.
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/servers/multiple-server-urls.mdx b/fern/products/asyncapi-def/pages/servers/multiple-server-urls.mdx
deleted file mode 100644
index 9842ed0a3..000000000
--- a/fern/products/asyncapi-def/pages/servers/multiple-server-urls.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Server URLs
----
-
-How to configure multiple server URLs for different environments.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/servers/naming-your-servers.mdx b/fern/products/asyncapi-def/pages/servers/naming-your-servers.mdx
deleted file mode 100644
index a5d0d1172..000000000
--- a/fern/products/asyncapi-def/pages/servers/naming-your-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Naming Your Servers
----
-
-Best practices for naming your server configurations.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/servers/overriding-servers.mdx b/fern/products/asyncapi-def/pages/servers/overriding-servers.mdx
deleted file mode 100644
index 0b5ecad00..000000000
--- a/fern/products/asyncapi-def/pages/servers/overriding-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Servers
----
-
-How to override server configurations in specific contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/asyncapi-def/pages/servers/overview.mdx b/fern/products/asyncapi-def/pages/servers/overview.mdx
deleted file mode 100644
index 4986039ff..000000000
--- a/fern/products/asyncapi-def/pages/servers/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Servers Overview
----
-
-Overview of server configuration options in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn).
\ No newline at end of file
diff --git a/fern/products/grpc-def/grpc-def.yml b/fern/products/grpc-def/grpc-def.yml
index 07abf61a7..be00164c1 100644
--- a/fern/products/grpc-def/grpc-def.yml
+++ b/fern/products/grpc-def/grpc-def.yml
@@ -1,61 +1,39 @@
navigation:
- - section: Getting Started
+ - page: Overview
+ path: ./pages/overview.mdx
+ - page: Authentication
+ path: ./pages/auth.mdx
+ - page: Servers
+ path: ./pages/servers.mdx
+ - section: Services
+ slug: services
contents:
- - page: Overview
- path: ./pages/getting-started/overview.mdx
- - page: Get to OpenAPI
- path: ./pages/getting-started/get-to-openapi.mdx
- - page: Philosophy
- path: ./pages/getting-started/philosophy.mdx
- - page: Customer Showcase
- path: ./pages/getting-started/customer-showcase.mdx
- - section: Get to OpenAPI
- contents:
- - page: Handwrite it
- path: ./pages/getting-started/get-to-openapi/handwrite-it.mdx
- - section: Generate it
- contents:
- - page: FastAPI
- path: ./pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
- - page: NestJS
- path: ./pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
- - page: Swaggo
- path: ./pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
- - page: Request a new framework
- path: ./pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
- - page: Overlay customizations
- path: ./pages/getting-started/overlay-customizations.mdx
- - section: Security Schemes
+ - page: gRPC Services
+ path: ./pages/services/grpc-services.mdx
+ slug: grpc-services
+ - page: Streaming
+ path: ./pages/services/streaming.mdx
+ slug: streaming
+ - page: Error Handling
+ path: ./pages/services/errors.mdx
+ slug: errors
+ - section: Extensions
+ slug: extensions
contents:
- - page: Overview
- path: ./pages/security-schemes/overview.mdx
- - page: Overriding the security scheme
- path: ./pages/security-schemes/overriding-security-scheme.mdx
- - page: Multiple security schemes
- path: ./pages/security-schemes/multiple-security-schemes.mdx
- - section: Servers
+ - page: Audiences
+ path: ./pages/extensions/audiences.mdx
+ - page: SDK Method Names
+ path: ./pages/extensions/method-names.mdx
+ slug: method-names
+ - page: Parameter Names
+ path: ./pages/extensions/parameter-names.mdx
+ - page: Other
+ path: ./pages/extensions/others.mdx
+ slug: others
+ - section: Workflow & Automation
+ skip-slug: true
contents:
- - page: Overview
- path: ./pages/servers/overview.mdx
- - page: Overriding the servers
- path: ./pages/servers/overriding-servers.mdx
- - page: Naming your servers
- path: ./pages/servers/naming-your-servers.mdx
- - page: Multiple server URLs
- path: ./pages/servers/multiple-server-urls.mdx
- - section: Schemas
- contents:
- - page: Objects
- path: ./pages/schemas/objects.mdx
- - page: AllOf
- path: ./pages/schemas/allof.mdx
- - page: OneOf
- path: ./pages/schemas/oneof.mdx
- - page: AnyOf
- path: ./pages/schemas/anyof.mdx
- - page: Enums
- path: ./pages/schemas/enums.mdx
- - page: Override the type
- path: ./pages/schemas/override-type.mdx
- - page: Inlined schemas
- path: ./pages/schemas/inlined-schemas.mdx
+ - page: Overlay Customizations
+ path: ./pages/overrides.mdx
+ - page: Sync your gRPC Specification
+ path: ./pages/automation.mdx
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/auth.mdx b/fern/products/grpc-def/pages/auth.mdx
new file mode 100644
index 000000000..81b27418e
--- /dev/null
+++ b/fern/products/grpc-def/pages/auth.mdx
@@ -0,0 +1,370 @@
+---
+title: Authentication
+subtitle: Configure authentication for gRPC services including TLS, JWT, and custom auth
+---
+
+gRPC supports various authentication mechanisms to secure your services. Authentication can be configured at the transport level (TLS) and at the application level (credentials, tokens, etc.).
+
+## Transport Security (TLS)
+
+gRPC strongly recommends using TLS for production services to ensure encrypted communication:
+
+```protobuf auth_service.proto
+syntax = "proto3";
+
+package auth.v1;
+
+// Authentication service
+service AuthService {
+ // Authenticate user and return JWT token
+ rpc Login(LoginRequest) returns (LoginResponse);
+
+ // Validate and refresh JWT token
+ rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
+
+ // Logout and invalidate token
+ rpc Logout(LogoutRequest) returns (google.protobuf.Empty);
+}
+
+message LoginRequest {
+ string email = 1;
+ string password = 2;
+}
+
+message LoginResponse {
+ string access_token = 1;
+ string refresh_token = 2;
+ int64 expires_in = 3;
+ User user = 4;
+}
+```
+
+Configure TLS in your server:
+
+```python title="server.py"
+import grpc
+from grpc import ssl_channel_credentials
+import auth_service_pb2_grpc
+
+def create_secure_server():
+ # Load TLS credentials
+ server_credentials = grpc.ssl_server_credentials([
+ (private_key, certificate_chain)
+ ])
+
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+ auth_service_pb2_grpc.add_AuthServiceServicer_to_server(
+ AuthServiceServicer(), server
+ )
+
+ # Listen on secure port
+ server.add_secure_port('[::]:443', server_credentials)
+ return server
+```
+
+## JWT Authentication
+
+Use JWT tokens for stateless authentication:
+
+```protobuf auth.proto
+syntax = "proto3";
+
+package auth.v1;
+
+// JWT claims for user authentication
+message JWTClaims {
+ string user_id = 1;
+ string email = 2;
+ repeated string roles = 3;
+ google.protobuf.Timestamp issued_at = 4;
+ google.protobuf.Timestamp expires_at = 5;
+}
+```
+
+Implement JWT authentication in your service:
+
+```python title="auth_service.py"
+import grpc
+import jwt
+from grpc import ServicerContext
+
+class AuthServiceServicer(auth_service_pb2_grpc.AuthServiceServicer):
+
+ def Login(self, request, context):
+ # Validate credentials
+ user = self.validate_credentials(request.email, request.password)
+ if not user:
+ context.set_code(grpc.StatusCode.UNAUTHENTICATED)
+ context.set_details('Invalid credentials')
+ return auth_service_pb2.LoginResponse()
+
+ # Generate JWT token
+ payload = {
+ 'user_id': user.id,
+ 'email': user.email,
+ 'roles': user.roles,
+ 'exp': datetime.utcnow() + timedelta(hours=1)
+ }
+
+ access_token = jwt.encode(payload, JWT_SECRET, algorithm='HS256')
+
+ return auth_service_pb2.LoginResponse(
+ access_token=access_token,
+ refresh_token=self.generate_refresh_token(user.id),
+ expires_in=3600,
+ user=user
+ )
+```
+
+## Interceptors for Authentication
+
+Use gRPC interceptors to handle authentication across all methods:
+
+```python title="auth_interceptor.py"
+import grpc
+import jwt
+from grpc import ServicerContext
+
+class AuthInterceptor(grpc.ServerInterceptor):
+
+ def __init__(self, jwt_secret, exempt_methods=None):
+ self.jwt_secret = jwt_secret
+ self.exempt_methods = exempt_methods or []
+
+ def intercept_service(self, continuation, handler_call_details):
+ method_name = handler_call_details.method
+
+ # Skip authentication for exempt methods
+ if method_name in self.exempt_methods:
+ return continuation(handler_call_details)
+
+ # Extract metadata
+ metadata = dict(handler_call_details.invocation_metadata)
+ authorization = metadata.get('authorization', '')
+
+ if not authorization.startswith('Bearer '):
+ return self._unauthenticated_response()
+
+ token = authorization[7:] # Remove 'Bearer ' prefix
+
+ try:
+ # Validate JWT token
+ payload = jwt.decode(token, self.jwt_secret, algorithms=['HS256'])
+
+ # Add user info to context
+ handler_call_details = handler_call_details._replace(
+ invocation_metadata=handler_call_details.invocation_metadata + (
+ ('user_id', payload['user_id']),
+ ('user_email', payload['email']),
+ )
+ )
+
+ return continuation(handler_call_details)
+
+ except jwt.ExpiredSignatureError:
+ return self._expired_token_response()
+ except jwt.InvalidTokenError:
+ return self._invalid_token_response()
+
+ def _unauthenticated_response(self):
+ def abort(ignored_request, context):
+ context.set_code(grpc.StatusCode.UNAUTHENTICATED)
+ context.set_details('Missing or invalid authorization header')
+ return grpc.unary_unary_rpc_method_handler(abort)
+```
+
+## API Key Authentication
+
+Implement API key-based authentication:
+
+```protobuf api_key.proto
+syntax = "proto3";
+
+package auth.v1;
+
+message ApiKeyRequest {
+ string api_key = 1;
+ string service_name = 2;
+}
+
+message ApiKeyResponse {
+ bool valid = 1;
+ string client_id = 2;
+ repeated string permissions = 3;
+ google.protobuf.Timestamp expires_at = 4;
+}
+```
+
+Server implementation:
+
+```python title="api_key_auth.py"
+class ApiKeyAuthInterceptor(grpc.ServerInterceptor):
+
+ def __init__(self, api_key_store):
+ self.api_key_store = api_key_store
+
+ def intercept_service(self, continuation, handler_call_details):
+ metadata = dict(handler_call_details.invocation_metadata)
+ api_key = metadata.get('x-api-key', '')
+
+ if not api_key:
+ return self._unauthorized_response('API key required')
+
+ # Validate API key
+ key_info = self.api_key_store.get(api_key)
+ if not key_info or key_info.is_expired():
+ return self._unauthorized_response('Invalid or expired API key')
+
+ # Add client info to context
+ handler_call_details = handler_call_details._replace(
+ invocation_metadata=handler_call_details.invocation_metadata + (
+ ('client_id', key_info.client_id),
+ ('permissions', ','.join(key_info.permissions)),
+ )
+ )
+
+ return continuation(handler_call_details)
+```
+
+## OAuth2 Integration
+
+Integrate with OAuth2 providers:
+
+```python title="oauth2_auth.py"
+import requests
+from google.oauth2 import id_token
+from google.auth.transport import requests as google_requests
+
+class OAuth2Interceptor(grpc.ServerInterceptor):
+
+ def __init__(self, google_client_id):
+ self.google_client_id = google_client_id
+
+ def intercept_service(self, continuation, handler_call_details):
+ metadata = dict(handler_call_details.invocation_metadata)
+ auth_header = metadata.get('authorization', '')
+
+ if not auth_header.startswith('Bearer '):
+ return self._unauthorized_response()
+
+ token = auth_header[7:]
+
+ try:
+ # Verify Google ID token
+ idinfo = id_token.verify_oauth2_token(
+ token, google_requests.Request(), self.google_client_id
+ )
+
+ # Add user info to context
+ handler_call_details = handler_call_details._replace(
+ invocation_metadata=handler_call_details.invocation_metadata + (
+ ('user_id', idinfo['sub']),
+ ('user_email', idinfo['email']),
+ ('user_name', idinfo.get('name', '')),
+ )
+ )
+
+ return continuation(handler_call_details)
+
+ except ValueError:
+ return self._invalid_token_response()
+```
+
+## Client-side Authentication
+
+Configure authentication on the client side:
+
+```python title="client.py"
+import grpc
+
+# TLS with JWT
+def create_authenticated_channel(server_address, jwt_token):
+ credentials = grpc.ssl_channel_credentials()
+ channel = grpc.secure_channel(server_address, credentials)
+
+ # Add JWT token to all requests
+ def jwt_interceptor(continuation, client_call_details):
+ metadata = list(client_call_details.metadata or [])
+ metadata.append(('authorization', f'Bearer {jwt_token}'))
+
+ client_call_details = client_call_details._replace(metadata=metadata)
+ return continuation(client_call_details)
+
+ intercepted_channel = grpc.intercept_channel(channel, jwt_interceptor)
+ return intercepted_channel
+
+# API Key authentication
+def create_api_key_channel(server_address, api_key):
+ credentials = grpc.ssl_channel_credentials()
+ channel = grpc.secure_channel(server_address, credentials)
+
+ def api_key_interceptor(continuation, client_call_details):
+ metadata = list(client_call_details.metadata or [])
+ metadata.append(('x-api-key', api_key))
+
+ client_call_details = client_call_details._replace(metadata=metadata)
+ return continuation(client_call_details)
+
+ intercepted_channel = grpc.intercept_channel(channel, api_key_interceptor)
+ return intercepted_channel
+```
+
+## Role-Based Access Control
+
+Implement RBAC for fine-grained permissions:
+
+```protobuf rbac.proto
+syntax = "proto3";
+
+package auth.v1;
+
+message Permission {
+ string resource = 1;
+ string action = 2;
+}
+
+message Role {
+ string name = 1;
+ repeated Permission permissions = 2;
+}
+
+message UserRoles {
+ string user_id = 1;
+ repeated string role_names = 2;
+}
+```
+
+RBAC interceptor implementation:
+
+```python title="rbac_interceptor.py"
+class RBACInterceptor(grpc.ServerInterceptor):
+
+ def __init__(self, permission_store):
+ self.permission_store = permission_store
+
+ def intercept_service(self, continuation, handler_call_details):
+ # Get user info from context (added by auth interceptor)
+ metadata = dict(handler_call_details.invocation_metadata)
+ user_id = metadata.get('user_id')
+
+ if not user_id:
+ return self._unauthorized_response()
+
+ # Check permissions for the method
+ method_name = handler_call_details.method
+ required_permission = self._get_required_permission(method_name)
+
+ if required_permission and not self._has_permission(user_id, required_permission):
+ return self._forbidden_response()
+
+ return continuation(handler_call_details)
+
+ def _has_permission(self, user_id, permission):
+ user_roles = self.permission_store.get_user_roles(user_id)
+ for role in user_roles:
+ if permission in role.permissions:
+ return True
+ return False
+```
+
+gRPC's flexible authentication system allows you to implement secure, scalable authentication patterns that work across different environments and use cases.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/automation.mdx b/fern/products/grpc-def/pages/automation.mdx
new file mode 100644
index 000000000..048c3b6b4
--- /dev/null
+++ b/fern/products/grpc-def/pages/automation.mdx
@@ -0,0 +1,472 @@
+---
+title: Sync your gRPC Specification
+subtitle: Automatically sync your Protocol Buffer changes to keep SDKs and docs up to date
+---
+
+Keeping your gRPC specifications in sync with your codebase is crucial for maintaining accurate SDKs and documentation. Fern provides several automation options to streamline this process.
+
+## GitHub Actions
+
+Use Fern's GitHub Action to automatically update SDKs and docs when your Protocol Buffer files change:
+
+```yaml title=".github/workflows/fern.yml"
+name: Fern
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'proto/**/*.proto'
+ - 'fern/**/*.yml'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - 'proto/**/*.proto'
+ - 'fern/**/*.yml'
+
+jobs:
+ fern-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Check gRPC spec
+ uses: fern-api/action@v0
+ with:
+ command: check
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+
+ fern-generate:
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Generate SDKs and docs
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Protocol Buffer validation
+
+Add validation steps for your Protocol Buffer files:
+
+```yaml title=".github/workflows/proto-validation.yml"
+name: Protocol Buffer Validation
+
+on:
+ push:
+ paths:
+ - 'proto/**/*.proto'
+ pull_request:
+ paths:
+ - 'proto/**/*.proto'
+
+jobs:
+ validate-proto:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup Protocol Buffer Compiler
+ uses: arduino/setup-protoc@v2
+ with:
+ version: '23.4'
+
+ - name: Validate Protocol Buffer files
+ run: |
+ find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
+
+ - name: Check for breaking changes
+ run: |
+ # Use buf for breaking change detection
+ buf breaking --against '.git#branch=main'
+
+ - name: Generate and validate with Fern
+ uses: fern-api/action@v0
+ with:
+ command: check
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Auto-sync from source
+
+Configure Fern to automatically pull Protocol Buffer files from various sources:
+
+### From Git repository
+```yaml title="generators.yml" {3-7}
+api:
+ specs:
+ - spec:
+ git:
+ repository: https://github.com/your-org/proto-definitions
+ path: services/user_service.proto
+ branch: main
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+### From local directory
+```yaml title="generators.yml" {3-4}
+api:
+ specs:
+ - spec: proto/user_service.proto
+ auto-sync: true
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## CI/CD integration
+
+### CircleCI
+```yaml title=".circleci/config.yml" {15-26}
+version: 2.1
+
+orbs:
+ fern: fernapi/fern@1.0
+
+workflows:
+ version: 2
+ build-and-generate:
+ jobs:
+ - build
+ - test:
+ requires:
+ - build
+ - validate-proto:
+ requires:
+ - build
+ - fern/generate:
+ requires:
+ - test
+ - validate-proto
+ filters:
+ branches:
+ only: main
+ context:
+ - fern-context
+
+jobs:
+ validate-proto:
+ docker:
+ - image: namely/protoc-all:1.51_1
+ steps:
+ - checkout
+ - run:
+ name: Validate Protocol Buffers
+ command: |
+ find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
+```
+
+### GitLab CI
+```yaml title=".gitlab-ci.yml" {13-25}
+stages:
+ - build
+ - test
+ - validate
+ - generate
+
+variables:
+ FERN_TOKEN: $FERN_TOKEN
+
+build:
+ stage: build
+ script:
+ - echo "Building gRPC service..."
+
+validate-proto:
+ stage: validate
+ image: namely/protoc-all:1.51_1
+ script:
+ - find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
+ only:
+ changes:
+ - proto/**/*.proto
+
+generate-sdks:
+ stage: generate
+ image: fernapi/fern:latest
+ script:
+ - fern generate
+ only:
+ - main
+```
+
+## Scheduled updates
+
+Set up scheduled updates to ensure your SDKs stay current:
+
+```yaml title=".github/workflows/scheduled-update.yml"
+name: Scheduled gRPC Update
+
+on:
+ schedule:
+ - cron: '0 2 * * 1' # Every Monday at 2 AM UTC
+ workflow_dispatch:
+
+jobs:
+ update-proto:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Sync Protocol Buffer files
+ run: |
+ # Sync from upstream proto repository
+ git subtree pull --prefix=proto https://github.com/your-org/proto-definitions main --squash
+
+ - name: Generate with latest spec
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+
+ - name: Create PR if changes
+ uses: peter-evans/create-pull-request@v5
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ commit-message: "chore: update Protocol Buffer definitions"
+ title: "Update Protocol Buffer definitions"
+ body: "Automated update of Protocol Buffer definitions from upstream repository"
+```
+
+## Code generation from gRPC server
+
+For servers that can generate their own Protocol Buffer definitions:
+
+```yaml title=".github/workflows/auto-generate.yml"
+name: Auto-generate from gRPC server
+
+on:
+ push:
+ paths:
+ - 'src/**/*.py' # Trigger on server code changes
+ - 'src/**/*.go'
+ - 'src/**/*.java'
+
+jobs:
+ generate-proto:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup environment
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+
+ - name: Install dependencies
+ run: |
+ pip install grpcio-tools
+
+ - name: Generate Protocol Buffer files
+ run: |
+ python -m grpc_tools.protoc \
+ --proto_path=src/protos \
+ --python_out=. \
+ --grpc_python_out=. \
+ --descriptor_set_out=proto/service.protoset \
+ src/protos/*.proto
+
+ - name: Convert to Protocol Buffer text format
+ run: |
+ protoc --decode_raw < proto/service.protoset > proto/user_service.proto
+
+ - name: Generate SDKs
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Monitoring changes
+
+Track changes to your Protocol Buffer specifications:
+
+```yaml title="generators.yml" {4-9}
+api:
+ specs:
+ - spec: proto/user_service.proto
+ change-detection:
+ enabled: true
+ breaking-changes: error
+ notifications:
+ slack: ${{ secrets.SLACK_WEBHOOK }}
+ email: team@yourcompany.com
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Multi-service sync
+
+Sync different Protocol Buffer services for different components:
+
+```yaml title="generators.yml" {3-7, 12-16, 21-25}
+environments:
+ user-service:
+ specs:
+ - spec: proto/user_service.proto
+ overlays:
+ - user-service-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@yourcompany/user-service-sdk"
+ order-service:
+ specs:
+ - spec: proto/order_service.proto
+ overlays:
+ - order-service-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@yourcompany/order-service-sdk"
+ payment-service:
+ specs:
+ - spec: proto/payment_service.proto
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@yourcompany/payment-service-sdk"
+```
+
+## Buf integration
+
+Use Buf for Protocol Buffer management and sync:
+
+```yaml title="buf.yaml"
+version: v1
+deps:
+ - buf.build/googleapis/googleapis
+ - buf.build/envoyproxy/protoc-gen-validate
+lint:
+ use:
+ - DEFAULT
+ except:
+ - UNARY_RPC
+breaking:
+ use:
+ - FILE
+```
+
+```yaml title=".github/workflows/buf-sync.yml"
+name: Buf Sync
+
+on:
+ push:
+ paths:
+ - 'proto/**/*.proto'
+ - 'buf.yaml'
+
+jobs:
+ buf-sync:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup Buf
+ uses: bufbuild/buf-setup-action@v1
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Lint Protocol Buffers
+ run: buf lint
+
+ - name: Check for breaking changes
+ run: buf breaking --against '.git#branch=main'
+
+ - name: Generate and push to Buf Registry
+ run: |
+ buf generate
+ buf push
+ env:
+ BUF_TOKEN: ${{ secrets.BUF_TOKEN }}
+
+ - name: Generate SDKs with Fern
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## gRPC reflection sync
+
+For services with gRPC reflection enabled:
+
+```python title="scripts/sync_from_reflection.py"
+import grpc
+from grpc_reflection.v1alpha import reflection_pb2
+from grpc_reflection.v1alpha import reflection_pb2_grpc
+import subprocess
+
+def sync_from_grpc_reflection(server_address, output_dir):
+ """Sync Protocol Buffer definitions from gRPC reflection"""
+
+ channel = grpc.insecure_channel(server_address)
+ reflection_stub = reflection_pb2_grpc.ServerReflectionStub(channel)
+
+ # List services
+ request = reflection_pb2.ServerReflectionRequest(
+ list_services=""
+ )
+
+ response = reflection_stub.ServerReflectionInfo(iter([request]))
+
+ for resp in response:
+ if resp.HasField('list_services_response'):
+ for service in resp.list_services_response.service:
+ print(f"Found service: {service.name}")
+
+ # Get file descriptor for service
+ file_request = reflection_pb2.ServerReflectionRequest(
+ file_containing_symbol=service.name
+ )
+
+ file_response = reflection_stub.ServerReflectionInfo(iter([file_request]))
+
+ for file_resp in file_response:
+ if file_resp.HasField('file_descriptor_response'):
+ # Save descriptor to file
+ descriptor_path = f"{output_dir}/{service.name}.protoset"
+ with open(descriptor_path, 'wb') as f:
+ f.write(file_resp.file_descriptor_response.file_descriptor_proto[0])
+
+ # Convert to text format
+ proto_path = f"{output_dir}/{service.name}.proto"
+ subprocess.run([
+ 'protoc',
+ '--decode_raw',
+ '--proto_path', output_dir,
+ descriptor_path
+ ], stdout=open(proto_path, 'w'))
+
+if __name__ == "__main__":
+ sync_from_grpc_reflection("localhost:50051", "proto/")
+```
+
+This ensures that any changes to your gRPC services are automatically reflected in your SDKs and documentation, maintaining consistency across your entire API ecosystem.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/extensions/audiences.mdx b/fern/products/grpc-def/pages/extensions/audiences.mdx
new file mode 100644
index 000000000..8c18f6ac9
--- /dev/null
+++ b/fern/products/grpc-def/pages/extensions/audiences.mdx
@@ -0,0 +1,360 @@
+---
+title: Use audiences to filter your API
+subtitle: Use `x-fern-audiences` to filter to relevant services, methods and messages
+---
+
+Audiences are a useful tool for segmenting your gRPC API for different consumers. Common examples of audiences include `public`
+and `beta`.
+
+
+ Remember to filter your SDKs and Docs after specifying audiences. If **no audiences** are specified,
+ nothing will be filtered.
+
+
+
+
+The following example configures the SDK to filter to the `public` audience:
+
+```yaml title="generators.yml" {3-4}
+groups:
+ sdks:
+ audiences:
+ - public
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+
+
+The following example configures the docs to filter to the `public` audience:
+
+```yaml title="docs.yml" {3-4}
+navigation:
+ - api: API Reference
+ audiences:
+ - public
+```
+
+
+
+
+
+## Filter services
+
+Add `x-fern-audiences` to services to control which services are included for specific audiences:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+// Public user service available to all clients
+service UserService {
+ option (x_fern_audiences) = "public";
+
+ rpc GetUser(GetUserRequest) returns (User);
+ rpc CreateUser(CreateUserRequest) returns (User);
+}
+
+// Internal admin service for administrative operations
+service AdminService {
+ option (x_fern_audiences) = "admin";
+
+ rpc ListAllUsers(ListAllUsersRequest) returns (ListAllUsersResponse);
+ rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
+ rpc BanUser(BanUserRequest) returns (google.protobuf.Empty);
+}
+```
+
+## Filter individual methods
+
+You can filter specific methods within services:
+
+```protobuf title="mixed_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Public method available to all
+ rpc GetUser(GetUserRequest) returns (User) {
+ option (x_fern_audiences) = "public";
+ }
+
+ // Beta method only for beta testers
+ rpc GetUserAnalytics(GetUserAnalyticsRequest) returns (UserAnalytics) {
+ option (x_fern_audiences) = "beta";
+ }
+
+ // Internal method for debugging
+ rpc GetUserDebugInfo(GetUserDebugRequest) returns (UserDebugInfo) {
+ option (x_fern_audiences) = "internal";
+ }
+}
+```
+
+## Filter message fields
+
+Filter specific fields within message types:
+
+```protobuf title="user_messages.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message User {
+ string id = 1; // Available to all audiences
+ string email = 2; // Available to all audiences
+ string name = 3; // Available to all audiences
+
+ // Admin-only fields
+ string internal_id = 4 [(x_fern_audiences) = "admin"];
+ repeated string permissions = 5 [(x_fern_audiences) = "admin"];
+ google.protobuf.Timestamp created_at = 6 [(x_fern_audiences) = "admin"];
+
+ // Debug-only fields
+ map debug_metadata = 7 [(x_fern_audiences) = "debug"];
+ string session_id = 8 [(x_fern_audiences) = "debug"];
+}
+```
+
+## Filter entire messages
+
+Filter entire message types to different audiences:
+
+```protobuf title="filtered_messages.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+// Public user data
+message PublicUser {
+ option (x_fern_audiences) = "public";
+
+ string id = 1;
+ string name = 2;
+ string avatar_url = 3;
+}
+
+// Extended user data for authenticated users
+message AuthenticatedUser {
+ option (x_fern_audiences) = "authenticated";
+
+ string id = 1;
+ string name = 2;
+ string email = 3;
+ string phone = 4;
+ UserPreferences preferences = 5;
+}
+
+// Full user data for administrators
+message AdminUser {
+ option (x_fern_audiences) = "admin";
+
+ string id = 1;
+ string name = 2;
+ string email = 3;
+ string phone = 4;
+ UserPreferences preferences = 5;
+ repeated string roles = 6;
+ repeated Permission permissions = 7;
+ google.protobuf.Timestamp created_at = 8;
+ google.protobuf.Timestamp last_login = 9;
+ string created_by = 10;
+}
+```
+
+## Filter enums and enum values
+
+Filter enum values based on audience:
+
+```protobuf title="user_enums.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+enum UserStatus {
+ USER_STATUS_UNSPECIFIED = 0;
+
+ // Public statuses
+ USER_STATUS_ACTIVE = 1 [(x_fern_audiences) = "public"];
+ USER_STATUS_INACTIVE = 2 [(x_fern_audiences) = "public"];
+
+ // Admin-only statuses
+ USER_STATUS_SUSPENDED = 3 [(x_fern_audiences) = "admin"];
+ USER_STATUS_BANNED = 4 [(x_fern_audiences) = "admin"];
+ USER_STATUS_PENDING_VERIFICATION = 5 [(x_fern_audiences) = "admin"];
+
+ // Internal debugging statuses
+ USER_STATUS_DEBUG = 6 [(x_fern_audiences) = "debug"];
+ USER_STATUS_TEST = 7 [(x_fern_audiences) = "debug"];
+}
+
+enum OperationType {
+ OPERATION_TYPE_UNSPECIFIED = 0;
+
+ // Available to beta users
+ OPERATION_TYPE_EXPERIMENTAL_FEATURE = 1 [(x_fern_audiences) = "beta"];
+ OPERATION_TYPE_ADVANCED_ANALYTICS = 2 [(x_fern_audiences) = "beta"];
+
+ // Internal operations
+ OPERATION_TYPE_SYSTEM_MAINTENANCE = 3 [(x_fern_audiences) = "internal"];
+}
+```
+
+## Request/Response filtering
+
+Filter request and response messages based on audience:
+
+```protobuf title="request_response.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Method with different request/response based on audience
+ rpc GetUserProfile(GetUserProfileRequest) returns (GetUserProfileResponse);
+}
+
+message GetUserProfileRequest {
+ string user_id = 1;
+
+ // Admin can request additional data
+ bool include_internal_data = 2 [(x_fern_audiences) = "admin"];
+ bool include_audit_log = 3 [(x_fern_audiences) = "admin"];
+
+ // Debug fields
+ bool include_debug_info = 4 [(x_fern_audiences) = "debug"];
+}
+
+message GetUserProfileResponse {
+ PublicUserProfile public_profile = 1; // Always included
+
+ AdminUserProfile admin_profile = 2 [(x_fern_audiences) = "admin"]; // Admin only
+ repeated AuditLogEntry audit_log = 3 [(x_fern_audiences) = "admin"]; // Admin only
+
+ DebugInfo debug_info = 4 [(x_fern_audiences) = "debug"]; // Debug only
+}
+```
+
+## Conditional method definitions
+
+Use audiences to expose different versions of methods:
+
+```protobuf title="conditional_methods.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Basic user creation for public
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option (x_fern_audiences) = "public";
+ }
+
+ // Enhanced user creation for admin
+ rpc CreateUserWithPermissions(CreateUserWithPermissionsRequest) returns (AdminUser) {
+ option (x_fern_audiences) = "admin";
+ }
+
+ // Bulk user creation for enterprise
+ rpc BulkCreateUsers(BulkCreateUsersRequest) returns (BulkCreateUsersResponse) {
+ option (x_fern_audiences) = "enterprise";
+ }
+
+ // Beta features
+ rpc CreateUserWithAI(CreateUserWithAIRequest) returns (User) {
+ option (x_fern_audiences) = "beta";
+ }
+}
+```
+
+## Streaming method filtering
+
+Filter streaming methods by audience:
+
+```protobuf title="streaming_methods.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserStreamingService {
+ // Public event streaming
+ rpc StreamUserEvents(StreamUserEventsRequest) returns (stream PublicUserEvent) {
+ option (x_fern_audiences) = "public";
+ }
+
+ // Admin event streaming with more details
+ rpc StreamAdminEvents(StreamAdminEventsRequest) returns (stream AdminEvent) {
+ option (x_fern_audiences) = "admin";
+ }
+
+ // Internal debugging stream
+ rpc StreamDebugEvents(StreamDebugEventsRequest) returns (stream DebugEvent) {
+ option (x_fern_audiences) = "debug";
+ }
+}
+```
+
+## Multiple audience support
+
+Assign multiple audiences to services or methods:
+
+```protobuf title="multiple_audiences.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Available to both public and authenticated users
+ rpc GetPublicUserInfo(GetPublicUserInfoRequest) returns (PublicUserInfo) {
+ option (x_fern_audiences) = "public,authenticated";
+ }
+
+ // Available to admin and support staff
+ rpc GetUserSupportInfo(GetUserSupportInfoRequest) returns (UserSupportInfo) {
+ option (x_fern_audiences) = "admin,support";
+ }
+
+ // Available to beta and internal users
+ rpc TestNewFeature(TestNewFeatureRequest) returns (TestNewFeatureResponse) {
+ option (x_fern_audiences) = "beta,internal";
+ }
+}
+```
+
+## Using custom extensions
+
+Define custom Fern extensions for audience filtering:
+
+```protobuf title="custom_extensions.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+import "google/protobuf/descriptor.proto";
+
+// Define custom option for audience filtering
+extend google.protobuf.ServiceOptions {
+ string x_fern_audiences = 50001;
+}
+
+extend google.protobuf.MethodOptions {
+ string x_fern_audiences = 50002;
+}
+
+extend google.protobuf.FieldOptions {
+ string x_fern_audiences = 50003;
+}
+
+extend google.protobuf.MessageOptions {
+ string x_fern_audiences = 50004;
+}
+
+extend google.protobuf.EnumValueOptions {
+ string x_fern_audiences = 50005;
+}
+```
+
+This allows you to create different views of the same gRPC API for different types of consumers, ensuring each audience only sees the services, methods, and data relevant to their use case.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/extensions/method-names.mdx b/fern/products/grpc-def/pages/extensions/method-names.mdx
new file mode 100644
index 000000000..3fe83e3a1
--- /dev/null
+++ b/fern/products/grpc-def/pages/extensions/method-names.mdx
@@ -0,0 +1,330 @@
+---
+title: SDK Method Names
+subtitle: Use `x-fern-sdk-method-name` to customize SDK method names for gRPC methods
+---
+
+By default, Fern generates SDK method names based on your gRPC service and method names. You can override this behavior using the `x-fern-sdk-method-name` extension.
+
+## Customize method names
+
+Use `x-fern-sdk-method-name` to specify custom method names for your gRPC methods:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Customize the SDK method name
+ rpc GetUserById(GetUserByIdRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "getUser";
+ }
+
+ // Another customized method name
+ rpc CreateNewUser(CreateNewUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "create";
+ }
+
+ // Remove redundant service name from method
+ rpc UpdateUserProfile(UpdateUserProfileRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "updateProfile";
+ }
+}
+```
+
+This will generate SDK methods like:
+
+```typescript
+// Instead of client.userService.getUserById()
+const user = await client.userService.getUser({ id: "user_123" });
+
+// Instead of client.userService.createNewUser()
+const newUser = await client.userService.create({ userData: {...} });
+
+// Instead of client.userService.updateUserProfile()
+const updatedUser = await client.userService.updateProfile({ profileData: {...} });
+```
+
+## Method naming conventions
+
+Follow these conventions when naming SDK methods:
+
+### CRUD operations
+Use standard CRUD naming for common operations:
+
+```protobuf title="crud_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "create";
+ }
+
+ rpc GetUser(GetUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "get";
+ }
+
+ rpc UpdateUser(UpdateUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "update";
+ }
+
+ rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty) {
+ option (x_fern_sdk_method_name) = "delete";
+ }
+
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {
+ option (x_fern_sdk_method_name) = "list";
+ }
+}
+```
+
+### Search and query operations
+Use descriptive names for search operations:
+
+```protobuf title="search_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse) {
+ option (x_fern_sdk_method_name) = "search";
+ }
+
+ rpc FindUserByEmail(FindUserByEmailRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "findByEmail";
+ }
+
+ rpc QueryUsersByRole(QueryUsersByRoleRequest) returns (QueryUsersResponse) {
+ option (x_fern_sdk_method_name) = "queryByRole";
+ }
+}
+```
+
+### Action operations
+Use action-oriented names for business operations:
+
+```protobuf title="action_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc ActivateUserAccount(ActivateUserAccountRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "activate";
+ }
+
+ rpc DeactivateUserAccount(DeactivateUserAccountRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "deactivate";
+ }
+
+ rpc ResetUserPassword(ResetUserPasswordRequest) returns (ResetPasswordResponse) {
+ option (x_fern_sdk_method_name) = "resetPassword";
+ }
+
+ rpc SendVerificationEmail(SendVerificationEmailRequest) returns (google.protobuf.Empty) {
+ option (x_fern_sdk_method_name) = "sendVerification";
+ }
+}
+```
+
+## Language-specific method names
+
+You can specify different method names for different programming languages:
+
+```protobuf title="language_specific.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc GetUserPreferences(GetUserPreferencesRequest) returns (UserPreferences) {
+ option (x_fern_sdk_method_name_python) = "get_preferences";
+ option (x_fern_sdk_method_name_typescript) = "getPreferences";
+ option (x_fern_sdk_method_name_go) = "GetPreferences";
+ option (x_fern_sdk_method_name_java) = "getPreferences";
+ option (x_fern_sdk_method_name_csharp) = "GetPreferences";
+ }
+
+ rpc UpdateUserPreferences(UpdateUserPreferencesRequest) returns (UserPreferences) {
+ option (x_fern_sdk_method_name_python) = "update_preferences";
+ option (x_fern_sdk_method_name_typescript) = "updatePreferences";
+ option (x_fern_sdk_method_name_go) = "UpdatePreferences";
+ option (x_fern_sdk_method_name_java) = "updatePreferences";
+ option (x_fern_sdk_method_name_csharp) = "UpdatePreferences";
+ }
+}
+```
+
+## Streaming method names
+
+For streaming methods, use appropriate naming:
+
+```protobuf title="streaming_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserStreamingService {
+ // Server streaming
+ rpc StreamUserEvents(StreamUserEventsRequest) returns (stream UserEvent) {
+ option (x_fern_sdk_method_name) = "streamEvents";
+ }
+
+ // Client streaming
+ rpc UploadUserData(stream UploadUserDataRequest) returns (UploadUserDataResponse) {
+ option (x_fern_sdk_method_name) = "upload";
+ }
+
+ // Bidirectional streaming
+ rpc ChatWithUser(stream ChatMessage) returns (stream ChatMessage) {
+ option (x_fern_sdk_method_name) = "chat";
+ }
+
+ // Real-time notifications
+ rpc SubscribeToNotifications(SubscribeRequest) returns (stream Notification) {
+ option (x_fern_sdk_method_name) = "subscribe";
+ }
+}
+```
+
+## Service-level naming
+
+Customize the service name for better SDK organization:
+
+```protobuf title="service_naming.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserManagementService {
+ option (x_fern_sdk_service_name) = "users";
+
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "create";
+ }
+
+ rpc GetUser(GetUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "get";
+ }
+}
+
+service AuthenticationService {
+ option (x_fern_sdk_service_name) = "auth";
+
+ rpc LoginUser(LoginUserRequest) returns (LoginResponse) {
+ option (x_fern_sdk_method_name) = "login";
+ }
+
+ rpc LogoutUser(LogoutUserRequest) returns (google.protobuf.Empty) {
+ option (x_fern_sdk_method_name) = "logout";
+ }
+}
+```
+
+This generates cleaner SDK interfaces:
+
+```typescript
+// Instead of client.userManagementService.createUser()
+await client.users.create({ userData: {...} });
+
+// Instead of client.authenticationService.loginUser()
+await client.auth.login({ credentials: {...} });
+```
+
+## Handling method overloading
+
+For services with similar methods, use descriptive names to avoid conflicts:
+
+```protobuf title="overloaded_methods.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Get user by ID
+ rpc GetUser(GetUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "get";
+ }
+
+ // Get user by email
+ rpc GetUserByEmail(GetUserByEmailRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "getByEmail";
+ }
+
+ // Get user by username
+ rpc GetUserByUsername(GetUserByUsernameRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "getByUsername";
+ }
+
+ // Get multiple users
+ rpc GetUsers(GetUsersRequest) returns (GetUsersResponse) {
+ option (x_fern_sdk_method_name) = "getMany";
+ }
+}
+```
+
+## Async method naming
+
+For long-running operations, consider async naming:
+
+```protobuf title="async_methods.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Quick operation
+ rpc GetUser(GetUserRequest) returns (User) {
+ option (x_fern_sdk_method_name) = "get";
+ }
+
+ // Long-running operation
+ rpc GenerateUserReport(GenerateUserReportRequest) returns (Operation) {
+ option (x_fern_sdk_method_name) = "generateReportAsync";
+ }
+
+ // Batch operation
+ rpc BatchUpdateUsers(BatchUpdateUsersRequest) returns (Operation) {
+ option (x_fern_sdk_method_name) = "batchUpdateAsync";
+ }
+}
+```
+
+## Grouping related methods
+
+Use consistent naming for related operations:
+
+```protobuf title="grouped_methods.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ // Profile operations
+ rpc GetUserProfile(GetUserProfileRequest) returns (UserProfile) {
+ option (x_fern_sdk_method_name) = "getProfile";
+ }
+
+ rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UserProfile) {
+ option (x_fern_sdk_method_name) = "updateProfile";
+ }
+
+ rpc DeleteUserProfile(DeleteUserProfileRequest) returns (google.protobuf.Empty) {
+ option (x_fern_sdk_method_name) = "deleteProfile";
+ }
+
+ // Settings operations
+ rpc GetUserSettings(GetUserSettingsRequest) returns (UserSettings) {
+ option (x_fern_sdk_method_name) = "getSettings";
+ }
+
+ rpc UpdateUserSettings(UpdateUserSettingsRequest) returns (UserSettings) {
+ option (x_fern_sdk_method_name) = "updateSettings";
+ }
+}
+```
+
+This ensures method names follow the conventions of each target language while maintaining clear and intuitive APIs for developers.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/extensions/others.mdx b/fern/products/grpc-def/pages/extensions/others.mdx
new file mode 100644
index 000000000..56d7dbf7c
--- /dev/null
+++ b/fern/products/grpc-def/pages/extensions/others.mdx
@@ -0,0 +1,403 @@
+---
+title: Other Extensions
+subtitle: Additional Fern extensions for gRPC specifications
+---
+
+Fern supports various extensions to enhance your gRPC specifications and improve the generated SDKs and documentation.
+
+## `x-fern-ignore`
+
+Use `x-fern-ignore` to exclude specific services, methods or messages from SDK generation:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc GetUser(GetUserRequest) returns (User);
+
+ // Exclude internal debugging method
+ rpc DebugUserData(DebugUserRequest) returns (DebugUserResponse) {
+ option (x_fern_ignore) = true;
+ }
+
+ // Exclude experimental method
+ rpc ExperimentalFeature(ExperimentalRequest) returns (ExperimentalResponse) {
+ option (x_fern_ignore) = true;
+ }
+}
+
+// Exclude internal message from SDK
+message InternalUserData {
+ option (x_fern_ignore) = true;
+
+ string internal_id = 1;
+ repeated string debug_flags = 2;
+ map system_metadata = 3;
+}
+```
+
+## `x-fern-examples`
+
+Provide additional examples for better SDK documentation:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option (x_fern_examples) = '{
+ "StandardUser": {
+ "description": "Create a regular user account",
+ "request": {
+ "email": "john@example.com",
+ "name": "John Doe",
+ "age": 30,
+ "preferences": {
+ "theme": "dark",
+ "notifications": true
+ }
+ },
+ "response": {
+ "id": "user_123",
+ "email": "john@example.com",
+ "name": "John Doe",
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ },
+ "AdminUser": {
+ "description": "Create an admin user with special permissions",
+ "request": {
+ "email": "admin@example.com",
+ "name": "Admin User",
+ "role": "admin",
+ "permissions": ["read", "write", "delete"]
+ },
+ "response": {
+ "id": "user_456",
+ "email": "admin@example.com",
+ "name": "Admin User",
+ "role": "admin",
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ }
+ }';
+ }
+}
+```
+
+## `x-fern-pagination`
+
+Configure pagination for methods that return lists:
+
+```protobuf title="pagination_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {
+ option (x_fern_pagination) = '{
+ "cursor": "page_token",
+ "results": "users",
+ "next_cursor": "next_page_token",
+ "has_next_page": "has_more"
+ }';
+ }
+}
+
+message ListUsersRequest {
+ int32 page_size = 1;
+ string page_token = 2;
+ string filter = 3;
+}
+
+message ListUsersResponse {
+ repeated User users = 1;
+ string next_page_token = 2;
+ bool has_more = 3;
+ int32 total_count = 4;
+}
+```
+
+## `x-fern-retry`
+
+Configure retry behavior for methods:
+
+```protobuf title="retry_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc ProcessPayment(ProcessPaymentRequest) returns (PaymentResult) {
+ option (x_fern_retry) = '{
+ "max_attempts": 3,
+ "exponential_backoff": true,
+ "initial_delay_ms": 1000,
+ "max_delay_ms": 30000,
+ "retry_on_status": ["UNAVAILABLE", "DEADLINE_EXCEEDED", "INTERNAL"]
+ }';
+ }
+
+ rpc UploadFile(UploadFileRequest) returns (UploadFileResponse) {
+ option (x_fern_retry) = '{
+ "max_attempts": 5,
+ "exponential_backoff": true,
+ "initial_delay_ms": 2000,
+ "max_delay_ms": 60000
+ }';
+ }
+}
+```
+
+## `x-fern-timeout`
+
+Configure timeout settings for methods:
+
+```protobuf title="timeout_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc GenerateReport(GenerateReportRequest) returns (ReportResult) {
+ option (x_fern_timeout) = '{
+ "seconds": 300,
+ "description": "Report generation can take up to 5 minutes"
+ }';
+ }
+
+ rpc QuickUserLookup(UserLookupRequest) returns (User) {
+ option (x_fern_timeout) = '{
+ "seconds": 5,
+ "description": "Quick lookup should complete within 5 seconds"
+ }';
+ }
+}
+```
+
+## `x-fern-error-handling`
+
+Configure error handling for methods:
+
+```protobuf title="error_handling_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option (x_fern_error_handling) = '{
+ "ALREADY_EXISTS": {
+ "error_name": "UserAlreadyExistsError",
+ "user_friendly_message": "A user with this email already exists"
+ },
+ "INVALID_ARGUMENT": {
+ "error_name": "ValidationError",
+ "user_friendly_message": "Please check your input and try again"
+ },
+ "RESOURCE_EXHAUSTED": {
+ "error_name": "RateLimitError",
+ "retry_after_seconds": 60
+ }
+ }';
+ }
+}
+```
+
+## `x-fern-availability`
+
+Mark features as available in specific SDK versions:
+
+```protobuf title="availability_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc GetUser(GetUserRequest) returns (User);
+
+ rpc GetUserAnalytics(GetUserAnalyticsRequest) returns (UserAnalytics) {
+ option (x_fern_availability) = '{
+ "status": "beta",
+ "message": "This feature is in beta and may change"
+ }';
+ }
+
+ rpc ExperimentalSearch(ExperimentalSearchRequest) returns (SearchResults) {
+ option (x_fern_availability) = '{
+ "status": "experimental",
+ "message": "This is an experimental feature and may be removed"
+ }';
+ }
+}
+```
+
+## `x-fern-streaming`
+
+Mark methods as streaming for appropriate SDK generation:
+
+```protobuf title="streaming_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ rpc StreamEvents(StreamEventsRequest) returns (stream Event) {
+ option (x_fern_streaming) = '{
+ "type": "server_streaming",
+ "termination": "client_closes"
+ }';
+ }
+
+ rpc UploadData(stream UploadDataRequest) returns (UploadResult) {
+ option (x_fern_streaming) = '{
+ "type": "client_streaming",
+ "termination": "client_closes"
+ }';
+ }
+
+ rpc Chat(stream ChatMessage) returns (stream ChatMessage) {
+ option (x_fern_streaming) = '{
+ "type": "bidirectional_streaming",
+ "termination": "either_closes"
+ }';
+ }
+}
+```
+
+## `x-fern-server-name`
+
+Specify custom names for different server environments:
+
+```protobuf title="server_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ option (x_fern_server_name_production) = "Production";
+ option (x_fern_server_name_staging) = "Staging";
+ option (x_fern_server_name_development) = "Development";
+
+ rpc GetUser(GetUserRequest) returns (User);
+}
+```
+
+## `x-fern-base-path`
+
+Configure base paths for generated SDK clients:
+
+```protobuf title="base_path_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ option (x_fern_base_path) = "/api/v1";
+
+ rpc GetUser(GetUserRequest) returns (User);
+}
+```
+
+## `x-fern-sdk-group-name`
+
+Group related services in the SDK:
+
+```protobuf title="grouped_services.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+service UserService {
+ option (x_fern_sdk_group_name) = "users";
+
+ rpc CreateUser(CreateUserRequest) returns (User);
+ rpc GetUser(GetUserRequest) returns (User);
+}
+
+service UserProfileService {
+ option (x_fern_sdk_group_name) = "users";
+
+ rpc GetProfile(GetProfileRequest) returns (UserProfile);
+ rpc UpdateProfile(UpdateProfileRequest) returns (UserProfile);
+}
+
+service AuthService {
+ option (x_fern_sdk_group_name) = "auth";
+
+ rpc Login(LoginRequest) returns (LoginResponse);
+ rpc Logout(LogoutRequest) returns (google.protobuf.Empty);
+}
+```
+
+This generates SDKs with grouped services:
+
+```typescript
+client.users.createUser(...)
+client.users.getUser(...)
+client.users.getProfile(...)
+client.users.updateProfile(...)
+client.auth.login(...)
+client.auth.logout(...)
+```
+
+## `x-fern-union-naming`
+
+Configure naming for oneof fields in SDKs:
+
+```protobuf title="union_naming.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message NotificationTarget {
+ oneof target {
+ option (x_fern_union_naming) = "discriminated";
+
+ string user_id = 1 [(x_fern_parameter_name) = "userId"];
+ string group_id = 2 [(x_fern_parameter_name) = "groupId"];
+ BroadcastTarget broadcast = 3;
+ }
+}
+```
+
+## `x-fern-validation`
+
+Add validation rules for message fields:
+
+```protobuf title="validation_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message CreateUserRequest {
+ string email = 1 [
+ (x_fern_validation) = '{
+ "format": "email",
+ "required": true
+ }'
+ ];
+
+ string name = 2 [
+ (x_fern_validation) = '{
+ "min_length": 1,
+ "max_length": 100,
+ "required": true
+ }'
+ ];
+
+ int32 age = 3 [
+ (x_fern_validation) = '{
+ "minimum": 0,
+ "maximum": 120
+ }'
+ ];
+}
+```
+
+These extensions help you create more robust and user-friendly SDKs while maintaining full control over the generated code structure and behavior.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/extensions/parameter-names.mdx b/fern/products/grpc-def/pages/extensions/parameter-names.mdx
new file mode 100644
index 000000000..de31f385f
--- /dev/null
+++ b/fern/products/grpc-def/pages/extensions/parameter-names.mdx
@@ -0,0 +1,302 @@
+---
+title: Parameter Names
+subtitle: Use `x-fern-parameter-name` to customize SDK parameter names for gRPC message fields
+---
+
+By default, Fern uses the field names from your Protocol Buffer message definitions as SDK parameter names. You can customize these using the `x-fern-parameter-name` extension.
+
+## Customize field names
+
+Use `x-fern-parameter-name` to specify custom parameter names for message fields:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message CreateUserRequest {
+ string email_address = 1 [(x_fern_parameter_name) = "email"];
+ string full_name = 2 [(x_fern_parameter_name) = "name"];
+ int32 age_in_years = 3 [(x_fern_parameter_name) = "age"];
+ string phone_number = 4 [(x_fern_parameter_name) = "phone"];
+ UserPreferences user_preferences = 5 [(x_fern_parameter_name) = "preferences"];
+}
+
+message User {
+ string user_id = 1 [(x_fern_parameter_name) = "id"];
+ string email_address = 2 [(x_fern_parameter_name) = "email"];
+ string full_name = 3 [(x_fern_parameter_name) = "name"];
+ google.protobuf.Timestamp created_timestamp = 4 [(x_fern_parameter_name) = "createdAt"];
+ google.protobuf.Timestamp updated_timestamp = 5 [(x_fern_parameter_name) = "updatedAt"];
+}
+```
+
+This generates SDK methods with cleaner parameter names:
+
+```typescript
+// Instead of email_address, full_name, age_in_years, phone_number, user_preferences
+await client.users.create({
+ email: "john@example.com",
+ name: "John Doe",
+ age: 30,
+ phone: "+1-555-0123",
+ preferences: { theme: "dark" }
+});
+```
+
+## Language-specific parameter names
+
+You can specify different parameter names for different programming languages:
+
+```protobuf title="language_specific.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message SearchUsersRequest {
+ string search_query = 1 [
+ (x_fern_parameter_name_python) = "search_query",
+ (x_fern_parameter_name_typescript) = "searchQuery",
+ (x_fern_parameter_name_go) = "SearchQuery",
+ (x_fern_parameter_name_java) = "searchQuery",
+ (x_fern_parameter_name_csharp) = "SearchQuery"
+ ];
+
+ int32 page_size = 2 [
+ (x_fern_parameter_name_python) = "page_size",
+ (x_fern_parameter_name_typescript) = "pageSize",
+ (x_fern_parameter_name_go) = "PageSize",
+ (x_fern_parameter_name_java) = "pageSize",
+ (x_fern_parameter_name_csharp) = "PageSize"
+ ];
+
+ string page_token = 3 [
+ (x_fern_parameter_name_python) = "page_token",
+ (x_fern_parameter_name_typescript) = "pageToken",
+ (x_fern_parameter_name_go) = "PageToken",
+ (x_fern_parameter_name_java) = "pageToken",
+ (x_fern_parameter_name_csharp) = "PageToken"
+ ];
+}
+```
+
+## Nested message naming
+
+Customize parameter names for nested message fields:
+
+```protobuf title="nested_messages.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message CreateUserRequest {
+ UserProfile user_profile = 1 [(x_fern_parameter_name) = "profile"];
+ ContactInfo contact_info = 2 [(x_fern_parameter_name) = "contact"];
+ AccountSettings account_settings = 3 [(x_fern_parameter_name) = "settings"];
+}
+
+message UserProfile {
+ string first_name = 1 [(x_fern_parameter_name) = "firstName"];
+ string last_name = 2 [(x_fern_parameter_name) = "lastName"];
+ string display_name = 3 [(x_fern_parameter_name) = "displayName"];
+ string bio_text = 4 [(x_fern_parameter_name) = "bio"];
+ string avatar_url = 5 [(x_fern_parameter_name) = "avatarUrl"];
+}
+
+message ContactInfo {
+ string email_address = 1 [(x_fern_parameter_name) = "email"];
+ string phone_number = 2 [(x_fern_parameter_name) = "phone"];
+ Address mailing_address = 3 [(x_fern_parameter_name) = "address"];
+}
+
+message Address {
+ string street_address = 1 [(x_fern_parameter_name) = "street"];
+ string city_name = 2 [(x_fern_parameter_name) = "city"];
+ string state_province = 3 [(x_fern_parameter_name) = "state"];
+ string postal_code = 4 [(x_fern_parameter_name) = "zipCode"];
+ string country_code = 5 [(x_fern_parameter_name) = "country"];
+}
+```
+
+## Repeated field naming
+
+Handle repeated fields with custom naming:
+
+```protobuf title="repeated_fields.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message User {
+ string user_id = 1 [(x_fern_parameter_name) = "id"];
+ repeated string role_names = 2 [(x_fern_parameter_name) = "roles"];
+ repeated string permission_codes = 3 [(x_fern_parameter_name) = "permissions"];
+ repeated SocialAccount social_accounts = 4 [(x_fern_parameter_name) = "socialAccounts"];
+ repeated string tag_list = 5 [(x_fern_parameter_name) = "tags"];
+}
+
+message SocialAccount {
+ string platform_name = 1 [(x_fern_parameter_name) = "platform"];
+ string account_handle = 2 [(x_fern_parameter_name) = "handle"];
+ string profile_url = 3 [(x_fern_parameter_name) = "url"];
+ bool is_verified = 4 [(x_fern_parameter_name) = "verified"];
+}
+
+message BatchCreateUsersRequest {
+ repeated CreateUserRequest user_requests = 1 [(x_fern_parameter_name) = "users"];
+ bool send_welcome_email = 2 [(x_fern_parameter_name) = "sendWelcomeEmail"];
+ bool skip_validation = 3 [(x_fern_parameter_name) = "skipValidation"];
+}
+```
+
+## Map field naming
+
+Customize names for map fields:
+
+```protobuf title="map_fields.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message UserPreferences {
+ map notification_settings = 1 [(x_fern_parameter_name) = "notifications"];
+ map feature_flags = 2 [(x_fern_parameter_name) = "features"];
+ map display_settings = 3 [(x_fern_parameter_name) = "display"];
+ map theme_overrides = 4 [(x_fern_parameter_name) = "themes"];
+}
+
+message MetricsData {
+ map performance_metrics = 1 [(x_fern_parameter_name) = "performance"];
+ map usage_counters = 2 [(x_fern_parameter_name) = "usage"];
+ map event_timestamps = 3 [(x_fern_parameter_name) = "events"];
+}
+```
+
+## Optional and required field naming
+
+Customize names for optional and required fields:
+
+```protobuf title="optional_fields.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message UpdateUserRequest {
+ // Required fields
+ string user_id = 1 [(x_fern_parameter_name) = "id"];
+
+ // Optional fields with custom names
+ optional string email_address = 2 [(x_fern_parameter_name) = "email"];
+ optional string full_name = 3 [(x_fern_parameter_name) = "name"];
+ optional int32 age_in_years = 4 [(x_fern_parameter_name) = "age"];
+ optional string profile_picture_url = 5 [(x_fern_parameter_name) = "avatarUrl"];
+
+ // Field mask for partial updates
+ google.protobuf.FieldMask update_mask = 6 [(x_fern_parameter_name) = "updateMask"];
+}
+```
+
+## Oneof field naming
+
+Handle oneof fields with meaningful names:
+
+```protobuf title="oneof_fields.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message SearchRequest {
+ oneof search_criteria {
+ string search_by_email = 1 [(x_fern_parameter_name) = "email"];
+ string search_by_username = 2 [(x_fern_parameter_name) = "username"];
+ string search_by_phone = 3 [(x_fern_parameter_name) = "phone"];
+ UserIdSearch search_by_id = 4 [(x_fern_parameter_name) = "id"];
+ }
+
+ SearchOptions search_options = 5 [(x_fern_parameter_name) = "options"];
+}
+
+message UserIdSearch {
+ string user_id = 1 [(x_fern_parameter_name) = "id"];
+ bool include_deleted = 2 [(x_fern_parameter_name) = "includeDeleted"];
+}
+
+message NotificationRequest {
+ oneof notification_target {
+ string target_user_id = 1 [(x_fern_parameter_name) = "userId"];
+ string target_group_id = 2 [(x_fern_parameter_name) = "groupId"];
+ BroadcastTarget target_broadcast = 3 [(x_fern_parameter_name) = "broadcast"];
+ }
+
+ string message_content = 4 [(x_fern_parameter_name) = "message"];
+ NotificationPriority priority_level = 5 [(x_fern_parameter_name) = "priority"];
+}
+```
+
+## Timestamp and duration naming
+
+Customize names for time-related fields:
+
+```protobuf title="time_fields.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message UserSession {
+ string session_id = 1 [(x_fern_parameter_name) = "id"];
+ string user_id = 2 [(x_fern_parameter_name) = "userId"];
+ google.protobuf.Timestamp created_timestamp = 3 [(x_fern_parameter_name) = "createdAt"];
+ google.protobuf.Timestamp last_accessed_timestamp = 4 [(x_fern_parameter_name) = "lastAccessedAt"];
+ google.protobuf.Timestamp expires_timestamp = 5 [(x_fern_parameter_name) = "expiresAt"];
+ google.protobuf.Duration session_duration = 6 [(x_fern_parameter_name) = "duration"];
+ google.protobuf.Duration idle_timeout = 7 [(x_fern_parameter_name) = "idleTimeout"];
+}
+
+message ScheduledTask {
+ string task_id = 1 [(x_fern_parameter_name) = "id"];
+ google.protobuf.Timestamp scheduled_time = 2 [(x_fern_parameter_name) = "scheduledAt"];
+ google.protobuf.Duration execution_timeout = 3 [(x_fern_parameter_name) = "timeout"];
+ google.protobuf.Duration retry_delay = 4 [(x_fern_parameter_name) = "retryDelay"];
+}
+```
+
+## Complex nested naming
+
+Handle complex nested structures:
+
+```protobuf title="complex_nested.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+message CreateOrderRequest {
+ OrderDetails order_details = 1 [(x_fern_parameter_name) = "order"];
+ PaymentInfo payment_info = 2 [(x_fern_parameter_name) = "payment"];
+ ShippingInfo shipping_info = 3 [(x_fern_parameter_name) = "shipping"];
+}
+
+message OrderDetails {
+ repeated OrderItem order_items = 1 [(x_fern_parameter_name) = "items"];
+ string promo_code = 2 [(x_fern_parameter_name) = "promoCode"];
+ string special_instructions = 3 [(x_fern_parameter_name) = "instructions"];
+}
+
+message OrderItem {
+ string product_id = 1 [(x_fern_parameter_name) = "productId"];
+ int32 quantity_ordered = 2 [(x_fern_parameter_name) = "quantity"];
+ double unit_price = 3 [(x_fern_parameter_name) = "price"];
+ repeated string product_options = 4 [(x_fern_parameter_name) = "options"];
+}
+
+message PaymentInfo {
+ oneof payment_method {
+ CreditCardInfo credit_card_info = 1 [(x_fern_parameter_name) = "creditCard"];
+ PayPalInfo paypal_info = 2 [(x_fern_parameter_name) = "paypal"];
+ BankTransferInfo bank_transfer_info = 3 [(x_fern_parameter_name) = "bankTransfer"];
+ }
+
+ BillingAddress billing_address = 4 [(x_fern_parameter_name) = "billingAddress"];
+}
+```
+
+Using custom parameter names ensures consistent naming conventions across your SDK while maintaining compatibility with your existing Protocol Buffer schemas.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/customer-showcase.mdx b/fern/products/grpc-def/pages/getting-started/customer-showcase.mdx
deleted file mode 100644
index 662e99e15..000000000
--- a/fern/products/grpc-def/pages/getting-started/customer-showcase.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Customer Showcase
----
-
-Examples and case studies of customers using AsyncAPI definitions successfully.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi.mdx
deleted file mode 100644
index 6a181a5a0..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Get to OpenAPI
----
-
-How to transition from AsyncAPI to OpenAPI or integrate both specifications.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
deleted file mode 100644
index 0bd4dc599..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: FastAPI
----
-
-Instructions for generating an OpenAPI definition from a FastAPI project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
deleted file mode 100644
index b40a35e56..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: NestJS
----
-
-Instructions for generating an OpenAPI definition from a NestJS project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
deleted file mode 100644
index 3df1b9d6b..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Request New Framework
----
-
-Don't see your framework? Let us know which framework you'd like to see supported for OpenAPI generation.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
deleted file mode 100644
index 2dfadfb00..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Swaggo
----
-
-Instructions for generating an OpenAPI definition from a Swaggo (Go) project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx b/fern/products/grpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
deleted file mode 100644
index 5555fe1f5..000000000
--- a/fern/products/grpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Handwrite It
----
-
-Step-by-step guide to manually writing your OpenAPI definition from scratch.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/overlay-customizations.mdx b/fern/products/grpc-def/pages/getting-started/overlay-customizations.mdx
deleted file mode 100644
index 6fb9aad86..000000000
--- a/fern/products/grpc-def/pages/getting-started/overlay-customizations.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overlay Customizations
----
-
-Learn how to customize your OpenAPI overlays to fit your unique requirements.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/overview.mdx b/fern/products/grpc-def/pages/getting-started/overview.mdx
deleted file mode 100644
index 1c576e43b..000000000
--- a/fern/products/grpc-def/pages/getting-started/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overview
----
-
-This is the overview page for Getting Started with your OpenAPI definition. Here you will find a high-level introduction and guidance on how to begin.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/getting-started/philosophy.mdx b/fern/products/grpc-def/pages/getting-started/philosophy.mdx
deleted file mode 100644
index 0e52bc615..000000000
--- a/fern/products/grpc-def/pages/getting-started/philosophy.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Philosophy
----
-
-The philosophy and principles behind AsyncAPI definitions and how they enhance API development.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/overrides.mdx b/fern/products/grpc-def/pages/overrides.mdx
new file mode 100644
index 000000000..beda30a91
--- /dev/null
+++ b/fern/products/grpc-def/pages/overrides.mdx
@@ -0,0 +1,432 @@
+---
+title: Overlay Customizations
+subtitle: Use overlay files to modify your gRPC specifications without editing the original Proto files
+---
+
+Overlays allow you to modify your gRPC specifications without directly editing the original Protocol Buffer files. This is useful for:
+- Adding Fern-specific extensions
+- Customizing SDK method and parameter names
+- Adding examples and documentation
+- Configuring audiences and filtering
+
+## Configure overlays
+
+To use overlays, add them to your `generators.yml` file:
+
+```yaml title="generators.yml" {3-5}
+api:
+ specs:
+ - spec: proto/user_service.proto
+ overlays:
+ - overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Overlay file structure
+
+Overlay files use Protocol Buffer text format with Fern extensions:
+
+```protobuf title="overlay.yml"
+# Add SDK method names to existing gRPC methods
+service UserService {
+ rpc CreateUser {
+ option (x_fern_sdk_method_name) = "create";
+ }
+
+ rpc GetUser {
+ option (x_fern_sdk_method_name) = "get";
+ }
+
+ rpc ListUsers {
+ option (x_fern_sdk_method_name) = "list";
+ option (x_fern_pagination) = '{
+ "cursor": "page_token",
+ "results": "users",
+ "next_cursor": "next_page_token"
+ }';
+ }
+}
+```
+
+## Add method names
+
+Override SDK method names for better developer experience:
+
+```protobuf title="overlay.yml"
+service UserService {
+ rpc GetUserById {
+ option (x_fern_sdk_method_name) = "getUser";
+ }
+
+ rpc CreateNewUser {
+ option (x_fern_sdk_method_name) = "create";
+ }
+
+ rpc UpdateUserProfile {
+ option (x_fern_sdk_method_name) = "updateProfile";
+ }
+
+ rpc DeleteUserAccount {
+ option (x_fern_sdk_method_name) = "delete";
+ }
+}
+
+service AuthenticationService {
+ option (x_fern_sdk_service_name) = "auth";
+
+ rpc LoginUser {
+ option (x_fern_sdk_method_name) = "login";
+ }
+
+ rpc LogoutUser {
+ option (x_fern_sdk_method_name) = "logout";
+ }
+}
+```
+
+## Add parameter names
+
+Customize parameter names for better SDK ergonomics:
+
+```protobuf title="overlay.yml"
+message CreateUserRequest {
+ string email_address {
+ option (x_fern_parameter_name) = "email";
+ }
+
+ string full_name {
+ option (x_fern_parameter_name) = "name";
+ }
+
+ int32 age_in_years {
+ option (x_fern_parameter_name) = "age";
+ }
+
+ UserPreferences user_preferences {
+ option (x_fern_parameter_name) = "preferences";
+ }
+}
+
+message User {
+ string user_id {
+ option (x_fern_parameter_name) = "id";
+ }
+
+ google.protobuf.Timestamp created_timestamp {
+ option (x_fern_parameter_name) = "createdAt";
+ }
+
+ google.protobuf.Timestamp updated_timestamp {
+ option (x_fern_parameter_name) = "updatedAt";
+ }
+}
+```
+
+## Add examples
+
+Enhance your specification with comprehensive examples:
+
+```protobuf title="overlay.yml"
+service UserService {
+ rpc CreateUser {
+ option (x_fern_examples) = '{
+ "StandardUser": {
+ "description": "Create a regular user account",
+ "request": {
+ "email": "john@example.com",
+ "name": "John Doe",
+ "age": 30,
+ "preferences": {
+ "theme": "dark",
+ "notifications": true
+ }
+ },
+ "response": {
+ "id": "user_123",
+ "email": "john@example.com",
+ "name": "John Doe",
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ },
+ "AdminUser": {
+ "description": "Create an admin user with permissions",
+ "request": {
+ "email": "admin@example.com",
+ "name": "Admin User",
+ "role": "admin",
+ "permissions": ["read", "write", "delete"]
+ },
+ "response": {
+ "id": "user_456",
+ "email": "admin@example.com",
+ "name": "Admin User",
+ "role": "admin",
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ }
+ }';
+ }
+}
+```
+
+## Filter with audiences
+
+Add audience filtering to services and methods:
+
+```protobuf title="overlay.yml"
+service UserService {
+ option (x_fern_audiences) = "public";
+
+ rpc GetUser {
+ option (x_fern_audiences) = "public";
+ }
+
+ rpc CreateUser {
+ option (x_fern_audiences) = "public";
+ }
+}
+
+service AdminService {
+ option (x_fern_audiences) = "admin";
+
+ rpc ListAllUsers {
+ option (x_fern_audiences) = "admin";
+ }
+
+ rpc DeleteUser {
+ option (x_fern_audiences) = "admin";
+ }
+}
+
+service DebugService {
+ option (x_fern_audiences) = "internal";
+
+ rpc GetSystemInfo {
+ option (x_fern_audiences) = "internal";
+ }
+}
+```
+
+## Add error handling
+
+Configure custom error handling for methods:
+
+```protobuf title="overlay.yml"
+service UserService {
+ rpc CreateUser {
+ option (x_fern_error_handling) = '{
+ "ALREADY_EXISTS": {
+ "error_name": "UserAlreadyExistsError",
+ "user_friendly_message": "A user with this email already exists"
+ },
+ "INVALID_ARGUMENT": {
+ "error_name": "ValidationError",
+ "user_friendly_message": "Please check your input and try again"
+ },
+ "RESOURCE_EXHAUSTED": {
+ "error_name": "RateLimitError",
+ "retry_after_seconds": 60
+ }
+ }';
+ }
+
+ rpc ProcessPayment {
+ option (x_fern_retry) = '{
+ "max_attempts": 3,
+ "exponential_backoff": true,
+ "initial_delay_ms": 1000,
+ "max_delay_ms": 30000
+ }';
+ }
+}
+```
+
+## Add documentation
+
+Enhance descriptions and documentation:
+
+```protobuf title="overlay.yml"
+service UserService {
+ option (x_fern_service_description) = "User management service for creating, updating, and managing user accounts with comprehensive authentication and profile management capabilities.";
+
+ rpc CreateUser {
+ option (x_fern_method_description) = "Create a new user account with email verification and automatic profile setup. Supports custom preferences and role assignment.";
+ }
+
+ rpc GetUser {
+ option (x_fern_method_description) = "Retrieve user information by ID with optional inclusion of profile data, preferences, and activity history.";
+ }
+
+ rpc UpdateUser {
+ option (x_fern_method_description) = "Update user profile information with field-level updates using Protocol Buffer field masks for efficient partial updates.";
+ }
+}
+```
+
+## Configure timeouts and retries
+
+Add timeout and retry configurations:
+
+```protobuf title="overlay.yml"
+service UserService {
+ rpc GenerateUserReport {
+ option (x_fern_timeout) = '{
+ "seconds": 300,
+ "description": "Report generation can take up to 5 minutes for large datasets"
+ }';
+ }
+
+ rpc QuickUserLookup {
+ option (x_fern_timeout) = '{
+ "seconds": 5,
+ "description": "Quick lookup operations should complete within 5 seconds"
+ }';
+ }
+
+ rpc ImportUsers {
+ option (x_fern_retry) = '{
+ "max_attempts": 5,
+ "exponential_backoff": true,
+ "initial_delay_ms": 2000,
+ "max_delay_ms": 60000,
+ "retry_on_status": ["UNAVAILABLE", "DEADLINE_EXCEEDED", "INTERNAL"]
+ }';
+ }
+}
+```
+
+## Streaming configurations
+
+Configure streaming methods:
+
+```protobuf title="overlay.yml"
+service UserStreamingService {
+ rpc StreamUserEvents {
+ option (x_fern_streaming) = '{
+ "type": "server_streaming",
+ "termination": "client_closes",
+ "description": "Real-time stream of user events with automatic reconnection"
+ }';
+ }
+
+ rpc UploadUserData {
+ option (x_fern_streaming) = '{
+ "type": "client_streaming",
+ "termination": "client_closes",
+ "max_message_size": "10MB"
+ }';
+ }
+
+ rpc ChatWithSupport {
+ option (x_fern_streaming) = '{
+ "type": "bidirectional_streaming",
+ "termination": "either_closes",
+ "heartbeat_interval": "30s"
+ }';
+ }
+}
+```
+
+## Multiple overlays
+
+You can apply multiple overlay files in sequence:
+
+```yaml title="generators.yml" {4-8}
+api:
+ specs:
+ - spec: proto/user_service.proto
+ overlays:
+ - base-overlay.yml
+ - sdk-overlay.yml
+ - docs-overlay.yml
+ - examples-overlay.yml
+```
+
+## Environment-specific overlays
+
+Use different overlays for different environments:
+
+```yaml title="generators.yml" {4-7, 12-15}
+groups:
+ production:
+ audiences: [public]
+ specs:
+ - spec: proto/user_service.proto
+ overlays:
+ - production-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ internal:
+ audiences: [admin, internal]
+ specs:
+ - spec: proto/user_service.proto
+ overlays:
+ - internal-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Field-level overlays
+
+Apply overlays to specific message fields:
+
+```protobuf title="overlay.yml"
+message UserPreferences {
+ map notification_settings {
+ option (x_fern_parameter_name) = "notifications";
+ option (x_fern_field_description) = "User notification preferences with support for email, SMS, and push notifications";
+ }
+
+ map feature_flags {
+ option (x_fern_parameter_name) = "features";
+ option (x_fern_field_description) = "Feature flags controlling access to beta and experimental features";
+ }
+
+ string preferred_language {
+ option (x_fern_parameter_name) = "language";
+ option (x_fern_validation) = '{
+ "enum": ["en", "es", "fr", "de", "zh"],
+ "default": "en"
+ }';
+ }
+}
+```
+
+## Validation overlays
+
+Add validation rules to existing fields:
+
+```protobuf title="overlay.yml"
+message CreateUserRequest {
+ string email {
+ option (x_fern_validation) = '{
+ "format": "email",
+ "required": true,
+ "description": "Must be a valid email address"
+ }';
+ }
+
+ string name {
+ option (x_fern_validation) = '{
+ "min_length": 1,
+ "max_length": 100,
+ "required": true,
+ "pattern": "^[a-zA-Z\\s]+$"
+ }';
+ }
+
+ int32 age {
+ option (x_fern_validation) = '{
+ "minimum": 0,
+ "maximum": 120,
+ "description": "Age in years"
+ }';
+ }
+}
+```
+
+Overlays are applied in order, allowing you to build up customizations incrementally while keeping your original Protocol Buffer files clean and focused on the core service definition.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/overview.mdx b/fern/products/grpc-def/pages/overview.mdx
new file mode 100644
index 000000000..a25da7a3c
--- /dev/null
+++ b/fern/products/grpc-def/pages/overview.mdx
@@ -0,0 +1,221 @@
+---
+title: What is gRPC?
+subtitle: gRPC is a high-performance RPC framework that uses Protocol Buffers
+---
+
+gRPC is a modern, open-source, high-performance Remote Procedure Call (RPC) framework that can run in any environment. It uses Protocol Buffers (protobuf) as the interface definition language and supports multiple programming languages.
+Fern is compatible with gRPC services and can generate SDKs and documentation from your `.proto` files.
+
+ Need help getting started with gRPC and Fern? Get live support [here](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)
+
+Below is an example of a gRPC service definition:
+
+```protobuf user_service.proto
+syntax = "proto3";
+
+package userservice.v1;
+
+// User service for managing user accounts
+service UserService {
+ // Create a new user account
+ rpc CreateUser(CreateUserRequest) returns (User);
+
+ // Get user by ID
+ rpc GetUser(GetUserRequest) returns (User);
+
+ // List users with pagination
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
+
+ // Update user information
+ rpc UpdateUser(UpdateUserRequest) returns (User);
+
+ // Delete a user account
+ rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
+
+ // Stream user events (server streaming)
+ rpc StreamUserEvents(StreamUserEventsRequest) returns (stream UserEvent);
+
+ // Upload user data (client streaming)
+ rpc UploadUserData(stream UploadUserDataRequest) returns (UploadUserDataResponse);
+
+ // Real-time chat (bidirectional streaming)
+ rpc Chat(stream ChatMessage) returns (stream ChatMessage);
+}
+
+// Messages for user operations
+message User {
+ string id = 1;
+ string email = 2;
+ string name = 3;
+ int32 age = 4;
+ google.protobuf.Timestamp created_at = 5;
+ UserStatus status = 6;
+}
+
+message CreateUserRequest {
+ string email = 1;
+ string name = 2;
+ int32 age = 3;
+}
+
+message GetUserRequest {
+ string id = 1;
+}
+
+message ListUsersRequest {
+ int32 page_size = 1;
+ string page_token = 2;
+ string filter = 3;
+}
+
+message ListUsersResponse {
+ repeated User users = 1;
+ string next_page_token = 2;
+ int32 total_count = 3;
+}
+
+message UpdateUserRequest {
+ string id = 1;
+ User user = 2;
+ google.protobuf.FieldMask update_mask = 3;
+}
+
+message DeleteUserRequest {
+ string id = 1;
+}
+
+message StreamUserEventsRequest {
+ string user_id = 1;
+ repeated UserEventType event_types = 2;
+}
+
+message UserEvent {
+ string id = 1;
+ string user_id = 2;
+ UserEventType type = 3;
+ google.protobuf.Timestamp timestamp = 4;
+ google.protobuf.Any data = 5;
+}
+
+message UploadUserDataRequest {
+ oneof data {
+ UserDataChunk chunk = 1;
+ UserDataMetadata metadata = 2;
+ }
+}
+
+message UserDataChunk {
+ bytes data = 1;
+ int64 offset = 2;
+}
+
+message UserDataMetadata {
+ string filename = 1;
+ int64 total_size = 2;
+ string content_type = 3;
+}
+
+message UploadUserDataResponse {
+ string file_id = 1;
+ int64 bytes_uploaded = 2;
+}
+
+message ChatMessage {
+ string id = 1;
+ string user_id = 2;
+ string room_id = 3;
+ string content = 4;
+ google.protobuf.Timestamp timestamp = 5;
+ ChatMessageType type = 6;
+}
+
+// Enums
+enum UserStatus {
+ USER_STATUS_UNSPECIFIED = 0;
+ USER_STATUS_ACTIVE = 1;
+ USER_STATUS_INACTIVE = 2;
+ USER_STATUS_SUSPENDED = 3;
+}
+
+enum UserEventType {
+ USER_EVENT_TYPE_UNSPECIFIED = 0;
+ USER_EVENT_TYPE_CREATED = 1;
+ USER_EVENT_TYPE_UPDATED = 2;
+ USER_EVENT_TYPE_DELETED = 3;
+ USER_EVENT_TYPE_LOGIN = 4;
+ USER_EVENT_TYPE_LOGOUT = 5;
+}
+
+enum ChatMessageType {
+ CHAT_MESSAGE_TYPE_UNSPECIFIED = 0;
+ CHAT_MESSAGE_TYPE_TEXT = 1;
+ CHAT_MESSAGE_TYPE_IMAGE = 2;
+ CHAT_MESSAGE_TYPE_FILE = 3;
+ CHAT_MESSAGE_TYPE_SYSTEM = 4;
+}
+```
+
+## gRPC Features
+
+gRPC offers several key features that make it ideal for modern applications:
+
+### High Performance
+- Binary protocol with Protocol Buffers
+- HTTP/2 transport for multiplexing and flow control
+- Efficient serialization and deserialization
+
+### Multiple Communication Patterns
+- **Unary RPCs**: Simple request/response
+- **Server Streaming**: Server sends stream of responses
+- **Client Streaming**: Client sends stream of requests
+- **Bidirectional Streaming**: Both sides send streams
+
+### Cross-Platform Support
+- Native support for 10+ programming languages
+- Generated client libraries and server stubs
+- Consistent API across all platforms
+
+## Setup your fern folder
+
+Start by initializing your fern folder with a gRPC service
+
+
+```sh protobuf
+fern init --proto ./path/to/service.proto
+```
+```sh directory
+fern init --proto-directory ./path/to/proto/files
+```
+
+
+This will initialize a directory like the following
+```
+fern/
+ ├─ fern.config.json
+ ├─ generators.yml
+ └─ proto/
+ ├─ user_service.proto
+ └─ common.proto
+```
+
+## gRPC vs REST
+
+| Feature | gRPC | REST |
+|---------|------|------|
+| Protocol | HTTP/2 | HTTP/1.1 |
+| Data Format | Protocol Buffers | JSON |
+| Performance | High | Moderate |
+| Streaming | Native support | Limited |
+| Browser Support | Limited | Full |
+| Schema | Strongly typed | Flexible |
+| Code Generation | Built-in | Third-party tools |
+
+## Common Use Cases
+
+gRPC is particularly well-suited for:
+
+- **Microservices Communication**: High-performance inter-service communication
+- **Real-time Applications**: Streaming for live updates and real-time features
+- **Mobile Applications**: Efficient mobile-to-server communication
+- **IoT Systems**: Lightweight communication for resource-constrained devices
+- **APIs with Strong Contracts**: Type-safe communication with schema evolution
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/allof.mdx b/fern/products/grpc-def/pages/schemas/allof.mdx
deleted file mode 100644
index 305b424b5..000000000
--- a/fern/products/grpc-def/pages/schemas/allof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: All Of
----
-
-How to use the `allOf` keyword to compose schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/anyof.mdx b/fern/products/grpc-def/pages/schemas/anyof.mdx
deleted file mode 100644
index bd293f9da..000000000
--- a/fern/products/grpc-def/pages/schemas/anyof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Any Of
----
-
-How to use the `anyOf` keyword to allow multiple possible schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/enums.mdx b/fern/products/grpc-def/pages/schemas/enums.mdx
deleted file mode 100644
index 269806428..000000000
--- a/fern/products/grpc-def/pages/schemas/enums.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Enums
----
-
-How to use enum types to define a fixed set of values in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/inlined-schemas.mdx b/fern/products/grpc-def/pages/schemas/inlined-schemas.mdx
deleted file mode 100644
index 50d864e10..000000000
--- a/fern/products/grpc-def/pages/schemas/inlined-schemas.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Inlined Schemas
----
-
-How to define schemas inline within your OpenAPI specification.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/objects.mdx b/fern/products/grpc-def/pages/schemas/objects.mdx
deleted file mode 100644
index 99c6a6e41..000000000
--- a/fern/products/grpc-def/pages/schemas/objects.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Objects
----
-
-How to define object schemas with properties in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/oneof.mdx b/fern/products/grpc-def/pages/schemas/oneof.mdx
deleted file mode 100644
index c29355abe..000000000
--- a/fern/products/grpc-def/pages/schemas/oneof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: One Of
----
-
-How to use the `oneOf` keyword to define alternative schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/schemas/override-type.mdx b/fern/products/grpc-def/pages/schemas/override-type.mdx
deleted file mode 100644
index 44cac7698..000000000
--- a/fern/products/grpc-def/pages/schemas/override-type.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Override Type
----
-
-How to override the default type inference for schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/security-schemes/multiple-security-schemes.mdx b/fern/products/grpc-def/pages/security-schemes/multiple-security-schemes.mdx
deleted file mode 100644
index 04a3407ed..000000000
--- a/fern/products/grpc-def/pages/security-schemes/multiple-security-schemes.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Security Schemes
----
-
-Learn how to define and use multiple security schemes in your OpenAPI definition.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/security-schemes/overriding-security-scheme.mdx b/fern/products/grpc-def/pages/security-schemes/overriding-security-scheme.mdx
deleted file mode 100644
index 905a45251..000000000
--- a/fern/products/grpc-def/pages/security-schemes/overriding-security-scheme.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Security Scheme
----
-
-How to override security schemes in specific operations or contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/security-schemes/overview.mdx b/fern/products/grpc-def/pages/security-schemes/overview.mdx
deleted file mode 100644
index 7743f8089..000000000
--- a/fern/products/grpc-def/pages/security-schemes/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Security Schemes Overview
----
-
-Overview of security schemes and authentication methods in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/servers.mdx b/fern/products/grpc-def/pages/servers.mdx
new file mode 100644
index 000000000..3e1b6c41d
--- /dev/null
+++ b/fern/products/grpc-def/pages/servers.mdx
@@ -0,0 +1,427 @@
+---
+title: Servers
+description: Configure gRPC servers with TLS, load balancing, and deployment options
+subtitle: Set up and configure gRPC servers for production deployments
+---
+
+gRPC servers can be configured with various options for security, performance, and scalability. Proper server configuration is crucial for production deployments.
+
+## Basic Server Setup
+
+Set up a basic gRPC server with multiple services:
+
+```python title="server.py"
+import grpc
+from concurrent import futures
+import user_service_pb2_grpc
+import auth_service_pb2_grpc
+from user_service import UserServiceServicer
+from auth_service import AuthServiceServicer
+
+def create_server():
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+
+ # Add services
+ user_service_pb2_grpc.add_UserServiceServicer_to_server(
+ UserServiceServicer(), server
+ )
+ auth_service_pb2_grpc.add_AuthServiceServicer_to_server(
+ AuthServiceServicer(), server
+ )
+
+ # Listen on insecure port for development
+ server.add_insecure_port('[::]:50051')
+
+ return server
+
+if __name__ == '__main__':
+ server = create_server()
+ server.start()
+ print("gRPC server started on port 50051")
+ server.wait_for_termination()
+```
+
+## TLS Configuration
+
+Configure TLS for secure production deployments:
+
+```python title="secure_server.py"
+import grpc
+from grpc import ssl_server_credentials
+
+def create_secure_server():
+ # Load TLS certificates
+ with open('server-key.pem', 'rb') as f:
+ private_key = f.read()
+ with open('server-cert.pem', 'rb') as f:
+ certificate_chain = f.read()
+ with open('ca-cert.pem', 'rb') as f:
+ root_certificates = f.read()
+
+ # Create server credentials
+ server_credentials = ssl_server_credentials(
+ [(private_key, certificate_chain)],
+ root_certificates=root_certificates,
+ require_client_auth=True # Mutual TLS
+ )
+
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=50))
+
+ # Add services
+ user_service_pb2_grpc.add_UserServiceServicer_to_server(
+ UserServiceServicer(), server
+ )
+
+ # Listen on secure port
+ server.add_secure_port('[::]:443', server_credentials)
+
+ return server
+```
+
+## Server Options
+
+Configure various server options for performance and behavior:
+
+```python title="configured_server.py"
+import grpc
+from grpc import compression
+
+def create_configured_server():
+ # Define server options
+ options = [
+ ('grpc.keepalive_time_ms', 30000),
+ ('grpc.keepalive_timeout_ms', 5000),
+ ('grpc.keepalive_permit_without_calls', True),
+ ('grpc.http2.max_pings_without_data', 0),
+ ('grpc.http2.min_time_between_pings_ms', 10000),
+ ('grpc.http2.min_ping_interval_without_data_ms', 300000),
+ ('grpc.max_connection_idle_ms', 60000),
+ ('grpc.max_connection_age_ms', 300000),
+ ('grpc.max_connection_age_grace_ms', 30000),
+ ('grpc.max_receive_message_length', 4 * 1024 * 1024),
+ ('grpc.max_send_message_length', 4 * 1024 * 1024),
+ ]
+
+ server = grpc.server(
+ futures.ThreadPoolExecutor(max_workers=100),
+ options=options,
+ compression=compression.Gzip
+ )
+
+ return server
+```
+
+## Health Checking
+
+Implement health checking for load balancer integration:
+
+```protobuf health.proto
+syntax = "proto3";
+
+package grpc.health.v1;
+
+service Health {
+ // Check health status
+ rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
+
+ // Watch health status changes
+ rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
+}
+
+message HealthCheckRequest {
+ string service = 1;
+}
+
+message HealthCheckResponse {
+ enum ServingStatus {
+ UNKNOWN = 0;
+ SERVING = 1;
+ NOT_SERVING = 2;
+ SERVICE_UNKNOWN = 3;
+ }
+ ServingStatus status = 1;
+}
+```
+
+Health service implementation:
+
+```python title="health_service.py"
+import grpc
+from grpc_health.v1 import health_pb2
+from grpc_health.v1 import health_pb2_grpc
+
+class HealthServicer(health_pb2_grpc.HealthServicer):
+
+ def __init__(self):
+ self._service_status = {}
+
+ def Check(self, request, context):
+ service_name = request.service
+ status = self._service_status.get(
+ service_name,
+ health_pb2.HealthCheckResponse.SERVING
+ )
+
+ return health_pb2.HealthCheckResponse(status=status)
+
+ def Watch(self, request, context):
+ # Implementation for streaming health updates
+ service_name = request.service
+
+ while not context.is_active():
+ status = self._service_status.get(
+ service_name,
+ health_pb2.HealthCheckResponse.SERVING
+ )
+
+ yield health_pb2.HealthCheckResponse(status=status)
+ time.sleep(5) # Check every 5 seconds
+
+ def set_service_status(self, service_name, status):
+ self._service_status[service_name] = status
+```
+
+## Reflection
+
+Enable gRPC reflection for development and debugging:
+
+```python title="reflection_server.py"
+import grpc
+from grpc_reflection.v1alpha import reflection
+
+def create_server_with_reflection():
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+
+ # Add services
+ user_service_pb2_grpc.add_UserServiceServicer_to_server(
+ UserServiceServicer(), server
+ )
+
+ # Enable reflection
+ SERVICE_NAMES = (
+ user_service_pb2.DESCRIPTOR.services_by_name['UserService'].full_name,
+ reflection.SERVICE_NAME,
+ )
+ reflection.enable_server_reflection(SERVICE_NAMES, server)
+
+ server.add_insecure_port('[::]:50051')
+ return server
+```
+
+## Load Balancing
+
+Configure client-side load balancing:
+
+```python title="load_balanced_client.py"
+import grpc
+
+def create_load_balanced_channel():
+ # DNS-based load balancing
+ channel = grpc.insecure_channel(
+ 'dns:///user-service.example.com:50051',
+ options=[
+ ('grpc.lb_policy_name', 'round_robin'),
+ ('grpc.dns_enable_srv_queries', True),
+ ]
+ )
+
+ return channel
+
+# Using a load balancer with multiple targets
+def create_multi_target_channel():
+ targets = [
+ 'user-service-1.example.com:50051',
+ 'user-service-2.example.com:50051',
+ 'user-service-3.example.com:50051',
+ ]
+
+ # Use a service mesh or load balancer
+ channel = grpc.insecure_channel(
+ f'ipv4:///{",".join(targets)}',
+ options=[('grpc.lb_policy_name', 'round_robin')]
+ )
+
+ return channel
+```
+
+## Kubernetes Deployment
+
+Deploy gRPC services on Kubernetes:
+
+```yaml title="grpc-service.yaml"
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: user-service
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: user-service
+ template:
+ metadata:
+ labels:
+ app: user-service
+ spec:
+ containers:
+ - name: user-service
+ image: user-service:latest
+ ports:
+ - containerPort: 50051
+ name: grpc
+ env:
+ - name: GRPC_PORT
+ value: "50051"
+ livenessProbe:
+ exec:
+ command: ["/bin/grpc_health_probe", "-addr=:50051"]
+ initialDelaySeconds: 30
+ readinessProbe:
+ exec:
+ command: ["/bin/grpc_health_probe", "-addr=:50051"]
+ initialDelaySeconds: 5
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: user-service
+spec:
+ selector:
+ app: user-service
+ ports:
+ - port: 50051
+ targetPort: 50051
+ name: grpc
+ type: ClusterIP
+```
+
+## Monitoring and Observability
+
+Add monitoring and tracing to your gRPC server:
+
+```python title="monitored_server.py"
+import grpc
+import time
+from prometheus_client import Counter, Histogram, start_http_server
+
+# Prometheus metrics
+REQUEST_COUNT = Counter(
+ 'grpc_requests_total',
+ 'Total gRPC requests',
+ ['method', 'status']
+)
+
+REQUEST_DURATION = Histogram(
+ 'grpc_request_duration_seconds',
+ 'gRPC request duration',
+ ['method']
+)
+
+class MonitoringInterceptor(grpc.ServerInterceptor):
+
+ def intercept_service(self, continuation, handler_call_details):
+ method = handler_call_details.method
+ start_time = time.time()
+
+ def monitor_wrapper(behavior):
+ def wrapper(request, context):
+ try:
+ response = behavior(request, context)
+ REQUEST_COUNT.labels(method=method, status='OK').inc()
+ return response
+ except Exception as e:
+ REQUEST_COUNT.labels(method=method, status='ERROR').inc()
+ raise
+ finally:
+ duration = time.time() - start_time
+ REQUEST_DURATION.labels(method=method).observe(duration)
+
+ return wrapper
+
+ return grpc.unary_unary_rpc_method_handler(
+ monitor_wrapper(continuation(handler_call_details).unary_unary)
+ )
+
+def create_monitored_server():
+ # Start Prometheus metrics server
+ start_http_server(8000)
+
+ server = grpc.server(
+ futures.ThreadPoolExecutor(max_workers=10),
+ interceptors=[MonitoringInterceptor()]
+ )
+
+ return server
+```
+
+## Environment-specific Configuration
+
+Configure servers for different environments:
+
+```python title="config.py"
+import os
+from dataclasses import dataclass
+
+@dataclass
+class ServerConfig:
+ port: int
+ max_workers: int
+ enable_tls: bool
+ cert_file: str = None
+ key_file: str = None
+ enable_reflection: bool = False
+ enable_health_check: bool = True
+
+def get_config() -> ServerConfig:
+ env = os.getenv('ENVIRONMENT', 'development')
+
+ if env == 'production':
+ return ServerConfig(
+ port=50051,
+ max_workers=100,
+ enable_tls=True,
+ cert_file='/etc/ssl/certs/server.crt',
+ key_file='/etc/ssl/private/server.key',
+ enable_reflection=False,
+ enable_health_check=True
+ )
+ elif env == 'staging':
+ return ServerConfig(
+ port=50051,
+ max_workers=50,
+ enable_tls=True,
+ cert_file='/etc/ssl/certs/staging.crt',
+ key_file='/etc/ssl/private/staging.key',
+ enable_reflection=True,
+ enable_health_check=True
+ )
+ else: # development
+ return ServerConfig(
+ port=50051,
+ max_workers=10,
+ enable_tls=False,
+ enable_reflection=True,
+ enable_health_check=True
+ )
+
+def create_server_from_config(config: ServerConfig):
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=config.max_workers))
+
+ # Add services...
+
+ if config.enable_tls:
+ # Configure TLS
+ with open(config.cert_file, 'rb') as f:
+ cert = f.read()
+ with open(config.key_file, 'rb') as f:
+ key = f.read()
+
+ credentials = grpc.ssl_server_credentials([(key, cert)])
+ server.add_secure_port(f'[::]:{config.port}', credentials)
+ else:
+ server.add_insecure_port(f'[::]:{config.port}')
+
+ return server
+```
+
+Proper server configuration ensures your gRPC services are secure, performant, and ready for production workloads.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/servers/multiple-server-urls.mdx b/fern/products/grpc-def/pages/servers/multiple-server-urls.mdx
deleted file mode 100644
index 94ba2c929..000000000
--- a/fern/products/grpc-def/pages/servers/multiple-server-urls.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Server URLs
----
-
-How to configure multiple server URLs for different environments.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/servers/naming-your-servers.mdx b/fern/products/grpc-def/pages/servers/naming-your-servers.mdx
deleted file mode 100644
index fd2a055bb..000000000
--- a/fern/products/grpc-def/pages/servers/naming-your-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Naming Your Servers
----
-
-Best practices for naming your server configurations.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/servers/overriding-servers.mdx b/fern/products/grpc-def/pages/servers/overriding-servers.mdx
deleted file mode 100644
index 0c5cc5f6f..000000000
--- a/fern/products/grpc-def/pages/servers/overriding-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Servers
----
-
-How to override server configurations in specific contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/servers/overview.mdx b/fern/products/grpc-def/pages/servers/overview.mdx
deleted file mode 100644
index 761a9d085..000000000
--- a/fern/products/grpc-def/pages/servers/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Servers Overview
----
-
-Overview of server configuration options in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/services/errors.mdx b/fern/products/grpc-def/pages/services/errors.mdx
new file mode 100644
index 000000000..91b3c15fd
--- /dev/null
+++ b/fern/products/grpc-def/pages/services/errors.mdx
@@ -0,0 +1,526 @@
+---
+title: Error Handling
+subtitle: Implement robust error handling with gRPC status codes and custom error details
+---
+
+gRPC provides a comprehensive error handling system using status codes, error messages, and optional error details. Proper error handling ensures clients can respond appropriately to different failure scenarios.
+
+## gRPC Status Codes
+
+gRPC uses standard status codes to indicate the outcome of RPC calls:
+
+```protobuf title="error_service.proto"
+syntax = "proto3";
+
+package errors.v1;
+
+import "google/rpc/status.proto";
+import "google/rpc/error_details.proto";
+
+service ErrorDemoService {
+ // Demonstrates various error scenarios
+ rpc ValidateUser(ValidateUserRequest) returns (ValidateUserResponse);
+
+ // May return rate limiting errors
+ rpc RateLimitedOperation(OperationRequest) returns (OperationResponse);
+
+ // May return resource exhausted errors
+ rpc ResourceIntensiveOperation(ResourceRequest) returns (ResourceResponse);
+}
+
+message ValidateUserRequest {
+ string user_id = 1;
+ string email = 2;
+}
+
+message ValidateUserResponse {
+ bool valid = 1;
+ repeated ValidationError errors = 2;
+}
+
+message ValidationError {
+ string field = 1;
+ string message = 2;
+ ErrorCode code = 3;
+}
+
+enum ErrorCode {
+ ERROR_CODE_UNSPECIFIED = 0;
+ ERROR_CODE_REQUIRED_FIELD = 1;
+ ERROR_CODE_INVALID_FORMAT = 2;
+ ERROR_CODE_DUPLICATE_VALUE = 3;
+ ERROR_CODE_OUT_OF_RANGE = 4;
+}
+```
+
+## Standard Error Handling
+
+Implement standard gRPC error responses:
+
+```python title="error_handling.py"
+import grpc
+from grpc import ServicerContext
+from google.rpc import status_pb2, error_details_pb2
+from google.protobuf import any_pb2
+
+class ErrorDemoServiceServicer(errors_pb2_grpc.ErrorDemoServiceServicer):
+
+ def ValidateUser(
+ self,
+ request: errors_pb2.ValidateUserRequest,
+ context: ServicerContext
+ ) -> errors_pb2.ValidateUserResponse:
+ """Demonstrate validation errors."""
+
+ validation_errors = []
+
+ # Check required fields
+ if not request.user_id:
+ validation_errors.append(errors_pb2.ValidationError(
+ field="user_id",
+ message="User ID is required",
+ code=errors_pb2.ERROR_CODE_REQUIRED_FIELD
+ ))
+
+ if not request.email:
+ validation_errors.append(errors_pb2.ValidationError(
+ field="email",
+ message="Email is required",
+ code=errors_pb2.ERROR_CODE_REQUIRED_FIELD
+ ))
+
+ # Validate email format
+ if request.email and not self._is_valid_email(request.email):
+ validation_errors.append(errors_pb2.ValidationError(
+ field="email",
+ message="Invalid email format",
+ code=errors_pb2.ERROR_CODE_INVALID_FORMAT
+ ))
+
+ # Check for duplicate user
+ if request.user_id and self.user_exists(request.user_id):
+ validation_errors.append(errors_pb2.ValidationError(
+ field="user_id",
+ message="User ID already exists",
+ code=errors_pb2.ERROR_CODE_DUPLICATE_VALUE
+ ))
+
+ # Return validation errors if any
+ if validation_errors:
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details("Validation failed")
+
+ return errors_pb2.ValidateUserResponse(
+ valid=False,
+ errors=validation_errors
+ )
+
+ return errors_pb2.ValidateUserResponse(valid=True)
+
+ def RateLimitedOperation(
+ self,
+ request: errors_pb2.OperationRequest,
+ context: ServicerContext
+ ) -> errors_pb2.OperationResponse:
+ """Demonstrate rate limiting errors."""
+
+ # Check rate limit
+ if not self.rate_limiter.is_allowed(request.client_id):
+ # Create detailed error information
+ retry_info = error_details_pb2.RetryInfo()
+ retry_info.retry_delay.seconds = 60 # Retry after 60 seconds
+
+ quota_failure = error_details_pb2.QuotaFailure()
+ violation = quota_failure.violations.add()
+ violation.subject = f"client:{request.client_id}"
+ violation.description = "API rate limit exceeded"
+
+ # Set error with details
+ context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED)
+ context.set_details("Rate limit exceeded")
+
+ # Add error details (rich error information)
+ self._add_error_details(context, [retry_info, quota_failure])
+
+ return errors_pb2.OperationResponse()
+
+ # Process operation normally
+ return self._process_operation(request)
+
+ def ResourceIntensiveOperation(
+ self,
+ request: errors_pb2.ResourceRequest,
+ context: ServicerContext
+ ) -> errors_pb2.ResourceResponse:
+ """Demonstrate resource exhaustion errors."""
+
+ try:
+ # Check system resources
+ if not self.resource_manager.has_capacity():
+ context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED)
+ context.set_details("System overloaded, please try again later")
+ return errors_pb2.ResourceResponse()
+
+ # Process intensive operation
+ result = self._process_intensive_operation(request)
+ return errors_pb2.ResourceResponse(result=result)
+
+ except TimeoutError:
+ context.set_code(grpc.StatusCode.DEADLINE_EXCEEDED)
+ context.set_details("Operation timed out")
+ return errors_pb2.ResourceResponse()
+
+ except PermissionError:
+ context.set_code(grpc.StatusCode.PERMISSION_DENIED)
+ context.set_details("Insufficient permissions for this operation")
+ return errors_pb2.ResourceResponse()
+
+ except FileNotFoundError as e:
+ context.set_code(grpc.StatusCode.NOT_FOUND)
+ context.set_details(f"Required resource not found: {str(e)}")
+ return errors_pb2.ResourceResponse()
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f"Internal server error: {str(e)}")
+ return errors_pb2.ResourceResponse()
+
+ def _add_error_details(self, context: ServicerContext, details):
+ """Add rich error details to gRPC response."""
+ rich_status = status_pb2.Status()
+ rich_status.code = context._state.code.value[0]
+ rich_status.message = context._state.details
+
+ for detail in details:
+ detail_any = any_pb2.Any()
+ detail_any.Pack(detail)
+ rich_status.details.append(detail_any)
+
+ # Add to trailing metadata
+ context.set_trailing_metadata([
+ ('grpc-status-details-bin', rich_status.SerializeToString())
+ ])
+```
+
+## Custom Error Types
+
+Define custom error types for domain-specific errors:
+
+```protobuf title="custom_errors.proto"
+syntax = "proto3";
+
+package errors.v1;
+
+// Custom error details
+message BusinessLogicError {
+ string error_code = 1;
+ string error_message = 2;
+ map error_context = 3;
+ repeated string suggested_actions = 4;
+}
+
+message ValidationFailure {
+ repeated FieldError field_errors = 1;
+ string global_error = 2;
+}
+
+message FieldError {
+ string field_path = 1;
+ string error_message = 2;
+ string error_code = 3;
+ google.protobuf.Any invalid_value = 4;
+}
+
+message ServiceUnavailableError {
+ string service_name = 1;
+ google.protobuf.Timestamp estimated_recovery_time = 2;
+ repeated string alternative_endpoints = 3;
+}
+```
+
+Custom error implementation:
+
+```python title="custom_errors.py"
+class CustomErrorHandler:
+
+ @staticmethod
+ def business_logic_error(
+ context: ServicerContext,
+ error_code: str,
+ message: str,
+ error_context: dict = None,
+ suggested_actions: list = None
+ ):
+ """Create a business logic error."""
+
+ business_error = errors_pb2.BusinessLogicError(
+ error_code=error_code,
+ error_message=message,
+ error_context=error_context or {},
+ suggested_actions=suggested_actions or []
+ )
+
+ context.set_code(grpc.StatusCode.FAILED_PRECONDITION)
+ context.set_details(message)
+
+ # Add custom error details
+ CustomErrorHandler._add_custom_details(context, business_error)
+
+ @staticmethod
+ def validation_failure(
+ context: ServicerContext,
+ field_errors: list,
+ global_error: str = None
+ ):
+ """Create a validation failure error."""
+
+ validation_error = errors_pb2.ValidationFailure(
+ field_errors=field_errors,
+ global_error=global_error or ""
+ )
+
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details("Validation failed")
+
+ CustomErrorHandler._add_custom_details(context, validation_error)
+
+ @staticmethod
+ def service_unavailable(
+ context: ServicerContext,
+ service_name: str,
+ estimated_recovery: datetime = None,
+ alternatives: list = None
+ ):
+ """Create a service unavailable error."""
+
+ error = errors_pb2.ServiceUnavailableError(
+ service_name=service_name,
+ alternative_endpoints=alternatives or []
+ )
+
+ if estimated_recovery:
+ error.estimated_recovery_time.FromDatetime(estimated_recovery)
+
+ context.set_code(grpc.StatusCode.UNAVAILABLE)
+ context.set_details(f"Service {service_name} is currently unavailable")
+
+ CustomErrorHandler._add_custom_details(context, error)
+
+ @staticmethod
+ def _add_custom_details(context: ServicerContext, error_detail):
+ """Add custom error details to response."""
+ detail_any = any_pb2.Any()
+ detail_any.Pack(error_detail)
+
+ rich_status = status_pb2.Status()
+ rich_status.code = context._state.code.value[0]
+ rich_status.message = context._state.details
+ rich_status.details.append(detail_any)
+
+ context.set_trailing_metadata([
+ ('grpc-status-details-bin', rich_status.SerializeToString())
+ ])
+
+# Usage example
+class UserService(user_pb2_grpc.UserServiceServicer):
+
+ def CreateUser(self, request, context):
+ # Validate business rules
+ if self.user_repository.email_exists(request.email):
+ CustomErrorHandler.business_logic_error(
+ context,
+ error_code="DUPLICATE_EMAIL",
+ message="Email address is already registered",
+ error_context={"email": request.email},
+ suggested_actions=[
+ "Use a different email address",
+ "Reset password if you forgot your account"
+ ]
+ )
+ return user_pb2.User()
+
+ # Continue with user creation...
+```
+
+## Error Interceptors
+
+Implement global error handling with interceptors:
+
+```python title="error_interceptor.py"
+import logging
+import traceback
+from grpc import ServicerContext, StatusCode
+
+class ErrorInterceptor(grpc.ServerInterceptor):
+
+ def __init__(self, logger=None):
+ self.logger = logger or logging.getLogger(__name__)
+
+ def intercept_service(self, continuation, handler_call_details):
+ def error_wrapper(behavior):
+ def wrapper(request, context):
+ try:
+ return behavior(request, context)
+
+ except ValueError as e:
+ # Convert Python ValueError to gRPC INVALID_ARGUMENT
+ context.set_code(StatusCode.INVALID_ARGUMENT)
+ context.set_details(f"Invalid input: {str(e)}")
+ self.logger.warning(f"Validation error in {handler_call_details.method}: {e}")
+ return self._get_default_response(behavior)
+
+ except PermissionError as e:
+ context.set_code(StatusCode.PERMISSION_DENIED)
+ context.set_details("Access denied")
+ self.logger.warning(f"Permission denied in {handler_call_details.method}: {e}")
+ return self._get_default_response(behavior)
+
+ except TimeoutError as e:
+ context.set_code(StatusCode.DEADLINE_EXCEEDED)
+ context.set_details("Operation timed out")
+ self.logger.error(f"Timeout in {handler_call_details.method}: {e}")
+ return self._get_default_response(behavior)
+
+ except Exception as e:
+ # Log unexpected errors
+ self.logger.error(
+ f"Unexpected error in {handler_call_details.method}: {e}\n"
+ f"Traceback: {traceback.format_exc()}"
+ )
+
+ context.set_code(StatusCode.INTERNAL)
+ context.set_details("Internal server error")
+ return self._get_default_response(behavior)
+
+ return wrapper
+
+ return grpc.unary_unary_rpc_method_handler(
+ error_wrapper(continuation(handler_call_details).unary_unary)
+ )
+
+ def _get_default_response(self, behavior):
+ """Return an empty response of the correct type."""
+ # This would need to be implemented based on your service methods
+ return None
+```
+
+## Client-side Error Handling
+
+Handle errors on the client side:
+
+```python title="client_error_handling.py"
+import grpc
+from google.rpc import status_pb2, error_details_pb2
+from google.protobuf import any_pb2
+
+def handle_grpc_errors(stub_method, request):
+ """Generic error handling for gRPC client calls."""
+
+ try:
+ response = stub_method(request)
+ return response, None
+
+ except grpc.RpcError as e:
+ error_info = {
+ 'code': e.code(),
+ 'details': e.details(),
+ 'status': e.code().name
+ }
+
+ # Extract rich error details if available
+ metadata = dict(e.trailing_metadata())
+ if 'grpc-status-details-bin' in metadata:
+ try:
+ status_detail = status_pb2.Status()
+ status_detail.ParseFromString(metadata['grpc-status-details-bin'])
+
+ error_info['rich_details'] = []
+ for detail in status_detail.details:
+ # Try to unpack common error types
+ if detail.Is(error_details_pb2.RetryInfo.DESCRIPTOR):
+ retry_info = error_details_pb2.RetryInfo()
+ detail.Unpack(retry_info)
+ error_info['rich_details'].append({
+ 'type': 'retry_info',
+ 'retry_delay_seconds': retry_info.retry_delay.seconds
+ })
+ elif detail.Is(error_details_pb2.QuotaFailure.DESCRIPTOR):
+ quota_failure = error_details_pb2.QuotaFailure()
+ detail.Unpack(quota_failure)
+ error_info['rich_details'].append({
+ 'type': 'quota_failure',
+ 'violations': [
+ {
+ 'subject': v.subject,
+ 'description': v.description
+ } for v in quota_failure.violations
+ ]
+ })
+
+ except Exception:
+ # If we can't parse rich details, that's okay
+ pass
+
+ return None, error_info
+
+# Usage example
+def create_user_with_error_handling(stub, user_data):
+ """Create user with comprehensive error handling."""
+
+ request = user_pb2.CreateUserRequest(**user_data)
+ response, error = handle_grpc_errors(stub.CreateUser, request)
+
+ if error:
+ if error['code'] == grpc.StatusCode.INVALID_ARGUMENT:
+ print(f"Validation failed: {error['details']}")
+ return None
+
+ elif error['code'] == grpc.StatusCode.ALREADY_EXISTS:
+ print(f"User already exists: {error['details']}")
+ return None
+
+ elif error['code'] == grpc.StatusCode.RESOURCE_EXHAUSTED:
+ # Check for retry information
+ for detail in error.get('rich_details', []):
+ if detail['type'] == 'retry_info':
+ retry_delay = detail['retry_delay_seconds']
+ print(f"Rate limited. Retry after {retry_delay} seconds")
+ return None
+
+ print("Resource exhausted")
+ return None
+
+ else:
+ print(f"Unexpected error: {error['status']} - {error['details']}")
+ return None
+
+ return response
+```
+
+## Error Response Patterns
+
+Define consistent error response patterns:
+
+```protobuf title="error_responses.proto"
+syntax = "proto3";
+
+// Standard error response envelope
+message ErrorResponse {
+ string error_code = 1;
+ string error_message = 2;
+ map error_context = 3;
+ repeated string suggestions = 4;
+ google.protobuf.Timestamp timestamp = 5;
+ string request_id = 6;
+}
+
+// Union response pattern
+message CreateUserResult {
+ oneof result {
+ User success = 1;
+ ErrorResponse error = 2;
+ }
+}
+```
+
+Proper error handling in gRPC ensures robust, maintainable services that provide clear feedback to clients about what went wrong and how to fix it.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/services/grpc-services.mdx b/fern/products/grpc-def/pages/services/grpc-services.mdx
new file mode 100644
index 000000000..0a80e65e3
--- /dev/null
+++ b/fern/products/grpc-def/pages/services/grpc-services.mdx
@@ -0,0 +1,419 @@
+---
+title: gRPC Services
+subtitle: Define gRPC services with RPCs, messages, and Protocol Buffer schemas
+---
+
+gRPC services are the core building blocks of your API. Each service defines a collection of remote procedure calls (RPCs) that clients can invoke, along with the message types used for requests and responses.
+
+## Service Definition
+
+Define a gRPC service in a `.proto` file:
+
+```protobuf title="user_service.proto"
+syntax = "proto3";
+
+package userservice.v1;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/field_mask.proto";
+
+// User management service
+service UserService {
+ // Create a new user account
+ rpc CreateUser(CreateUserRequest) returns (User) {
+ option deprecated = false;
+ }
+
+ // Get user by ID
+ rpc GetUser(GetUserRequest) returns (User);
+
+ // Update user information
+ rpc UpdateUser(UpdateUserRequest) returns (User);
+
+ // Delete a user account
+ rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
+
+ // List users with pagination
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
+
+ // Search users by various criteria
+ rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse);
+}
+
+// User message definition
+message User {
+ string id = 1;
+ string email = 2;
+ string name = 3;
+ int32 age = 4;
+ UserStatus status = 5;
+ google.protobuf.Timestamp created_at = 6;
+ google.protobuf.Timestamp updated_at = 7;
+ repeated string roles = 8;
+ UserPreferences preferences = 9;
+}
+
+// User status enumeration
+enum UserStatus {
+ USER_STATUS_UNSPECIFIED = 0;
+ USER_STATUS_ACTIVE = 1;
+ USER_STATUS_INACTIVE = 2;
+ USER_STATUS_SUSPENDED = 3;
+ USER_STATUS_DELETED = 4;
+}
+
+// Nested message for user preferences
+message UserPreferences {
+ bool email_notifications = 1;
+ string timezone = 2;
+ string language = 3;
+ ThemeMode theme = 4;
+}
+
+enum ThemeMode {
+ THEME_MODE_UNSPECIFIED = 0;
+ THEME_MODE_LIGHT = 1;
+ THEME_MODE_DARK = 2;
+ THEME_MODE_AUTO = 3;
+}
+```
+
+## Request and Response Messages
+
+Define clear request and response message types:
+
+```protobuf title="user_messages.proto"
+// Create user request
+message CreateUserRequest {
+ string email = 1 [(validate.rules).string.email = true];
+ string name = 2 [(validate.rules).string.min_len = 1];
+ int32 age = 3 [(validate.rules).int32.gte = 0];
+ UserPreferences preferences = 4;
+}
+
+// Get user request
+message GetUserRequest {
+ string id = 1 [(validate.rules).string.uuid = true];
+}
+
+// Update user request
+message UpdateUserRequest {
+ string id = 1 [(validate.rules).string.uuid = true];
+ User user = 2;
+ google.protobuf.FieldMask update_mask = 3;
+}
+
+// Delete user request
+message DeleteUserRequest {
+ string id = 1 [(validate.rules).string.uuid = true];
+}
+
+// List users request with pagination
+message ListUsersRequest {
+ int32 page_size = 1 [(validate.rules).int32 = {gte: 1, lte: 100}];
+ string page_token = 2;
+ string filter = 3;
+ string order_by = 4;
+}
+
+// List users response
+message ListUsersResponse {
+ repeated User users = 1;
+ string next_page_token = 2;
+ int32 total_count = 3;
+}
+
+// Search users request
+message SearchUsersRequest {
+ string query = 1 [(validate.rules).string.min_len = 1];
+ repeated UserStatus status_filter = 2;
+ repeated string role_filter = 3;
+ int32 page_size = 4 [(validate.rules).int32 = {gte: 1, lte: 100}];
+ string page_token = 5;
+}
+
+// Search users response
+message SearchUsersResponse {
+ repeated User users = 1;
+ string next_page_token = 2;
+ int32 total_count = 3;
+ SearchMetadata metadata = 4;
+}
+
+message SearchMetadata {
+ int32 search_time_ms = 1;
+ repeated string suggested_corrections = 2;
+}
+```
+
+## Service Implementation
+
+Implement the service in your preferred language:
+
+```python title="user_service.py"
+import grpc
+from grpc import ServicerContext
+import user_service_pb2
+import user_service_pb2_grpc
+from google.protobuf import empty_pb2
+from typing import Iterator
+
+class UserServiceServicer(user_service_pb2_grpc.UserServiceServicer):
+
+ def __init__(self, user_repository):
+ self.user_repository = user_repository
+
+ def CreateUser(
+ self,
+ request: user_service_pb2.CreateUserRequest,
+ context: ServicerContext
+ ) -> user_service_pb2.User:
+ """Create a new user account."""
+ try:
+ # Validate request
+ if not request.email or not request.name:
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details('Email and name are required')
+ return user_service_pb2.User()
+
+ # Check if user already exists
+ if self.user_repository.get_by_email(request.email):
+ context.set_code(grpc.StatusCode.ALREADY_EXISTS)
+ context.set_details(f'User with email {request.email} already exists')
+ return user_service_pb2.User()
+
+ # Create user
+ user = self.user_repository.create_user(
+ email=request.email,
+ name=request.name,
+ age=request.age,
+ preferences=request.preferences
+ )
+
+ return user
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to create user: {str(e)}')
+ return user_service_pb2.User()
+
+ def GetUser(
+ self,
+ request: user_service_pb2.GetUserRequest,
+ context: ServicerContext
+ ) -> user_service_pb2.User:
+ """Get user by ID."""
+ try:
+ user = self.user_repository.get_by_id(request.id)
+ if not user:
+ context.set_code(grpc.StatusCode.NOT_FOUND)
+ context.set_details(f'User with ID {request.id} not found')
+ return user_service_pb2.User()
+
+ return user
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to get user: {str(e)}')
+ return user_service_pb2.User()
+
+ def UpdateUser(
+ self,
+ request: user_service_pb2.UpdateUserRequest,
+ context: ServicerContext
+ ) -> user_service_pb2.User:
+ """Update user information."""
+ try:
+ # Check if user exists
+ existing_user = self.user_repository.get_by_id(request.id)
+ if not existing_user:
+ context.set_code(grpc.StatusCode.NOT_FOUND)
+ context.set_details(f'User with ID {request.id} not found')
+ return user_service_pb2.User()
+
+ # Apply field mask for partial updates
+ updated_user = self.user_repository.update_user(
+ user_id=request.id,
+ updates=request.user,
+ field_mask=request.update_mask
+ )
+
+ return updated_user
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to update user: {str(e)}')
+ return user_service_pb2.User()
+
+ def DeleteUser(
+ self,
+ request: user_service_pb2.DeleteUserRequest,
+ context: ServicerContext
+ ) -> empty_pb2.Empty:
+ """Delete a user account."""
+ try:
+ # Check if user exists
+ user = self.user_repository.get_by_id(request.id)
+ if not user:
+ context.set_code(grpc.StatusCode.NOT_FOUND)
+ context.set_details(f'User with ID {request.id} not found')
+ return empty_pb2.Empty()
+
+ # Soft delete user
+ self.user_repository.delete_user(request.id)
+
+ return empty_pb2.Empty()
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to delete user: {str(e)}')
+ return empty_pb2.Empty()
+
+ def ListUsers(
+ self,
+ request: user_service_pb2.ListUsersRequest,
+ context: ServicerContext
+ ) -> user_service_pb2.ListUsersResponse:
+ """List users with pagination."""
+ try:
+ # Apply pagination
+ page_size = min(request.page_size or 20, 100)
+
+ users, next_page_token, total_count = self.user_repository.list_users(
+ page_size=page_size,
+ page_token=request.page_token,
+ filter_expr=request.filter,
+ order_by=request.order_by
+ )
+
+ return user_service_pb2.ListUsersResponse(
+ users=users,
+ next_page_token=next_page_token,
+ total_count=total_count
+ )
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to list users: {str(e)}')
+ return user_service_pb2.ListUsersResponse()
+
+ def SearchUsers(
+ self,
+ request: user_service_pb2.SearchUsersRequest,
+ context: ServicerContext
+ ) -> user_service_pb2.SearchUsersResponse:
+ """Search users by various criteria."""
+ try:
+ start_time = time.time()
+
+ users, next_page_token, total_count = self.user_repository.search_users(
+ query=request.query,
+ status_filter=request.status_filter,
+ role_filter=request.role_filter,
+ page_size=request.page_size or 20,
+ page_token=request.page_token
+ )
+
+ search_time_ms = int((time.time() - start_time) * 1000)
+
+ metadata = user_service_pb2.SearchMetadata(
+ search_time_ms=search_time_ms,
+ suggested_corrections=[] # Add spell check suggestions if needed
+ )
+
+ return user_service_pb2.SearchUsersResponse(
+ users=users,
+ next_page_token=next_page_token,
+ total_count=total_count,
+ metadata=metadata
+ )
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Failed to search users: {str(e)}')
+ return user_service_pb2.SearchUsersResponse()
+```
+
+## Protocol Buffer Best Practices
+
+### Field Numbers
+- Use field numbers 1-15 for frequently used fields (more efficient encoding)
+- Reserve field numbers for removed fields to maintain compatibility
+- Never reuse field numbers
+
+```protobuf
+message User {
+ // Frequently used fields (1-15)
+ string id = 1;
+ string email = 2;
+ string name = 3;
+
+ // Less frequently used fields
+ UserPreferences preferences = 16;
+ repeated string tags = 17;
+
+ // Reserved fields
+ reserved 4, 5, 6;
+ reserved "old_field_name", "deprecated_field";
+}
+```
+
+### Naming Conventions
+- Use `snake_case` for field names
+- Use `PascalCase` for message and service names
+- Use `UPPER_SNAKE_CASE` for enum values
+
+```protobuf
+service UserManagementService { // PascalCase
+ rpc GetUser(GetUserRequest) returns (User);
+}
+
+message User { // PascalCase
+ string first_name = 1; // snake_case
+ UserStatus status = 2;
+}
+
+enum UserStatus {
+ USER_STATUS_UNSPECIFIED = 0; // UPPER_SNAKE_CASE
+ USER_STATUS_ACTIVE = 1;
+}
+```
+
+### Versioning
+- Include version in package names
+- Use semantic versioning for breaking changes
+
+```protobuf
+syntax = "proto3";
+
+package userservice.v1; // Version in package name
+
+option go_package = "example.com/userservice/v1";
+```
+
+## Multiple Services
+
+Organize related functionality into separate services:
+
+```protobuf title="services.proto"
+// User management
+service UserService {
+ rpc CreateUser(CreateUserRequest) returns (User);
+ rpc GetUser(GetUserRequest) returns (User);
+}
+
+// Authentication
+service AuthService {
+ rpc Login(LoginRequest) returns (LoginResponse);
+ rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
+}
+
+// Notification service
+service NotificationService {
+ rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse);
+ rpc GetNotificationPreferences(GetNotificationPreferencesRequest) returns (NotificationPreferences);
+}
+```
+
+gRPC services provide a strongly-typed, high-performance foundation for building distributed systems with clear contracts between clients and servers.
\ No newline at end of file
diff --git a/fern/products/grpc-def/pages/services/streaming.mdx b/fern/products/grpc-def/pages/services/streaming.mdx
new file mode 100644
index 000000000..69344d7a0
--- /dev/null
+++ b/fern/products/grpc-def/pages/services/streaming.mdx
@@ -0,0 +1,514 @@
+---
+title: Streaming
+subtitle: Implement server streaming, client streaming, and bidirectional streaming with gRPC
+---
+
+gRPC supports four types of service methods: unary, server streaming, client streaming, and bidirectional streaming. Streaming enables efficient real-time communication and large data transfers.
+
+## Server Streaming
+
+Server streaming allows the server to send multiple responses to a single client request:
+
+```protobuf title="streaming_service.proto"
+syntax = "proto3";
+
+package streaming.v1;
+
+service StreamingService {
+ // Server streaming: send multiple events to client
+ rpc StreamUserEvents(StreamUserEventsRequest) returns (stream UserEvent);
+
+ // Server streaming: download large file in chunks
+ rpc DownloadFile(DownloadFileRequest) returns (stream FileChunk);
+
+ // Server streaming: real-time notifications
+ rpc SubscribeToNotifications(SubscribeRequest) returns (stream Notification);
+}
+
+message StreamUserEventsRequest {
+ string user_id = 1;
+ repeated UserEventType event_types = 2;
+ google.protobuf.Timestamp start_time = 3;
+}
+
+message UserEvent {
+ string id = 1;
+ string user_id = 2;
+ UserEventType type = 3;
+ google.protobuf.Timestamp timestamp = 4;
+ google.protobuf.Any data = 5;
+}
+
+enum UserEventType {
+ USER_EVENT_TYPE_UNSPECIFIED = 0;
+ USER_EVENT_TYPE_LOGIN = 1;
+ USER_EVENT_TYPE_LOGOUT = 2;
+ USER_EVENT_TYPE_PROFILE_UPDATE = 3;
+ USER_EVENT_TYPE_PASSWORD_CHANGE = 4;
+}
+```
+
+Server streaming implementation:
+
+```python title="server_streaming.py"
+import grpc
+import time
+import asyncio
+from grpc import ServicerContext
+from typing import Iterator
+
+class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer):
+
+ def StreamUserEvents(
+ self,
+ request: streaming_pb2.StreamUserEventsRequest,
+ context: ServicerContext
+ ) -> Iterator[streaming_pb2.UserEvent]:
+ """Stream user events in real-time."""
+
+ # Validate request
+ if not request.user_id:
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details('User ID is required')
+ return
+
+ # Subscribe to event stream
+ event_subscriber = self.event_store.subscribe(
+ user_id=request.user_id,
+ event_types=request.event_types,
+ start_time=request.start_time
+ )
+
+ try:
+ while context.is_active():
+ # Wait for next event (with timeout)
+ try:
+ event = event_subscriber.get_next_event(timeout=30)
+ if event:
+ yield event
+ else:
+ # Send heartbeat to keep connection alive
+ continue
+
+ except TimeoutError:
+ # Check if client is still connected
+ if not context.is_active():
+ break
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Error streaming events: {str(e)}')
+ break
+
+ finally:
+ # Clean up subscription
+ event_subscriber.close()
+
+ def DownloadFile(
+ self,
+ request: streaming_pb2.DownloadFileRequest,
+ context: ServicerContext
+ ) -> Iterator[streaming_pb2.FileChunk]:
+ """Download file in chunks."""
+
+ try:
+ file_path = self.file_store.get_file_path(request.file_id)
+ if not file_path or not os.path.exists(file_path):
+ context.set_code(grpc.StatusCode.NOT_FOUND)
+ context.set_details(f'File {request.file_id} not found')
+ return
+
+ chunk_size = 64 * 1024 # 64KB chunks
+
+ with open(file_path, 'rb') as f:
+ while True:
+ chunk_data = f.read(chunk_size)
+ if not chunk_data:
+ break
+
+ # Check if client disconnected
+ if not context.is_active():
+ break
+
+ yield streaming_pb2.FileChunk(
+ data=chunk_data,
+ offset=f.tell() - len(chunk_data),
+ size=len(chunk_data)
+ )
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Error downloading file: {str(e)}')
+```
+
+## Client Streaming
+
+Client streaming allows the client to send multiple requests and receive a single response:
+
+```protobuf title="client_streaming.proto"
+service StreamingService {
+ // Client streaming: upload large file in chunks
+ rpc UploadFile(stream FileChunk) returns (UploadFileResponse);
+
+ // Client streaming: batch data processing
+ rpc ProcessDataBatch(stream DataRecord) returns (BatchProcessingResult);
+
+ // Client streaming: real-time metrics collection
+ rpc CollectMetrics(stream MetricData) returns (MetricsCollectionResult);
+}
+
+message FileChunk {
+ oneof data {
+ FileMetadata metadata = 1;
+ bytes chunk = 2;
+ }
+}
+
+message FileMetadata {
+ string filename = 1;
+ int64 total_size = 2;
+ string content_type = 3;
+ string checksum = 4;
+}
+
+message UploadFileResponse {
+ string file_id = 1;
+ int64 bytes_uploaded = 2;
+ string download_url = 3;
+ bool checksum_verified = 4;
+}
+```
+
+Client streaming implementation:
+
+```python title="client_streaming.py"
+class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer):
+
+ def UploadFile(
+ self,
+ request_iterator: Iterator[streaming_pb2.FileChunk],
+ context: ServicerContext
+ ) -> streaming_pb2.UploadFileResponse:
+ """Upload file from client stream."""
+
+ file_metadata = None
+ total_bytes = 0
+ file_path = None
+ hasher = hashlib.sha256()
+
+ try:
+ for chunk in request_iterator:
+ if chunk.HasField('metadata'):
+ # First chunk contains metadata
+ file_metadata = chunk.metadata
+
+ # Create temporary file
+ file_id = str(uuid.uuid4())
+ file_path = f'/tmp/uploads/{file_id}'
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
+
+ elif chunk.HasField('chunk'):
+ # Subsequent chunks contain file data
+ if not file_metadata:
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details('File metadata must be sent first')
+ return streaming_pb2.UploadFileResponse()
+
+ # Write chunk to file
+ with open(file_path, 'ab') as f:
+ f.write(chunk.chunk)
+
+ total_bytes += len(chunk.chunk)
+ hasher.update(chunk.chunk)
+
+ # Check size limit
+ if total_bytes > file_metadata.total_size:
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+ context.set_details('File size exceeds declared size')
+ return streaming_pb2.UploadFileResponse()
+
+ # Verify checksum
+ computed_checksum = hasher.hexdigest()
+ checksum_verified = computed_checksum == file_metadata.checksum
+
+ if not checksum_verified:
+ context.set_code(grpc.StatusCode.DATA_LOSS)
+ context.set_details('File checksum verification failed')
+ return streaming_pb2.UploadFileResponse()
+
+ # Move file to permanent storage
+ permanent_path = self.file_store.store_file(file_id, file_path)
+ download_url = self.file_store.get_download_url(file_id)
+
+ return streaming_pb2.UploadFileResponse(
+ file_id=file_id,
+ bytes_uploaded=total_bytes,
+ download_url=download_url,
+ checksum_verified=True
+ )
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Error uploading file: {str(e)}')
+ return streaming_pb2.UploadFileResponse()
+
+ finally:
+ # Clean up temporary file
+ if file_path and os.path.exists(file_path):
+ os.remove(file_path)
+```
+
+## Bidirectional Streaming
+
+Bidirectional streaming allows both client and server to send multiple messages:
+
+```protobuf title="bidirectional_streaming.proto"
+service StreamingService {
+ // Bidirectional streaming: real-time chat
+ rpc Chat(stream ChatMessage) returns (stream ChatMessage);
+
+ // Bidirectional streaming: real-time collaboration
+ rpc Collaborate(stream CollaborationEvent) returns (stream CollaborationEvent);
+
+ // Bidirectional streaming: live data processing
+ rpc ProcessLiveData(stream DataInput) returns (stream ProcessingResult);
+}
+
+message ChatMessage {
+ string id = 1;
+ string user_id = 2;
+ string room_id = 3;
+ string content = 4;
+ google.protobuf.Timestamp timestamp = 5;
+ ChatMessageType type = 6;
+}
+
+enum ChatMessageType {
+ CHAT_MESSAGE_TYPE_UNSPECIFIED = 0;
+ CHAT_MESSAGE_TYPE_TEXT = 1;
+ CHAT_MESSAGE_TYPE_IMAGE = 2;
+ CHAT_MESSAGE_TYPE_FILE = 3;
+ CHAT_MESSAGE_TYPE_SYSTEM = 4;
+ CHAT_MESSAGE_TYPE_TYPING = 5;
+}
+```
+
+Bidirectional streaming implementation:
+
+```python title="bidirectional_streaming.py"
+import asyncio
+import queue
+import threading
+
+class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer):
+
+ def Chat(
+ self,
+ request_iterator: Iterator[streaming_pb2.ChatMessage],
+ context: ServicerContext
+ ) -> Iterator[streaming_pb2.ChatMessage]:
+ """Bidirectional chat streaming."""
+
+ # Message queue for outgoing messages
+ outgoing_queue = queue.Queue()
+
+ # Track user session
+ user_session = None
+
+ def handle_incoming_messages():
+ """Process incoming messages from client."""
+ nonlocal user_session
+
+ try:
+ for message in request_iterator:
+ if not user_session:
+ # First message establishes session
+ user_session = self.chat_service.join_room(
+ user_id=message.user_id,
+ room_id=message.room_id
+ )
+
+ # Send welcome message
+ welcome_msg = streaming_pb2.ChatMessage(
+ id=str(uuid.uuid4()),
+ user_id="system",
+ room_id=message.room_id,
+ content=f"User {message.user_id} joined the chat",
+ timestamp=google.protobuf.timestamp_pb2.Timestamp(),
+ type=streaming_pb2.CHAT_MESSAGE_TYPE_SYSTEM
+ )
+ outgoing_queue.put(welcome_msg)
+
+ # Process message
+ if message.type == streaming_pb2.CHAT_MESSAGE_TYPE_TEXT:
+ # Broadcast to other users in room
+ self.chat_service.broadcast_message(message)
+
+ elif message.type == streaming_pb2.CHAT_MESSAGE_TYPE_TYPING:
+ # Send typing indicator to other users
+ self.chat_service.broadcast_typing(message)
+
+ except Exception as e:
+ print(f"Error handling incoming messages: {e}")
+ finally:
+ # Clean up session
+ if user_session:
+ self.chat_service.leave_room(user_session)
+
+ # Start background thread for incoming messages
+ incoming_thread = threading.Thread(target=handle_incoming_messages)
+ incoming_thread.daemon = True
+ incoming_thread.start()
+
+ # Subscribe to room messages
+ message_subscriber = None
+ if user_session:
+ message_subscriber = self.chat_service.subscribe_to_room(
+ user_session.room_id,
+ exclude_user=user_session.user_id
+ )
+
+ try:
+ while context.is_active():
+ # Check for outgoing messages from queue
+ try:
+ message = outgoing_queue.get(timeout=1)
+ yield message
+ except queue.Empty:
+ pass
+
+ # Check for messages from other users
+ if message_subscriber:
+ try:
+ room_message = message_subscriber.get_message(timeout=1)
+ if room_message:
+ yield room_message
+ except TimeoutError:
+ pass
+
+ finally:
+ # Clean up
+ if message_subscriber:
+ message_subscriber.close()
+ if user_session:
+ self.chat_service.leave_room(user_session)
+```
+
+## Streaming Best Practices
+
+### Flow Control
+Implement proper flow control to prevent overwhelming clients:
+
+```python title="flow_control.py"
+def StreamData(self, request, context):
+ """Stream with flow control."""
+
+ # Use a bounded queue to control memory usage
+ data_queue = queue.Queue(maxsize=100)
+
+ def data_producer():
+ """Background thread that produces data."""
+ for item in self.data_source.get_items():
+ try:
+ data_queue.put(item, timeout=5)
+ except queue.Full:
+ # Apply backpressure
+ print("Client is too slow, dropping data")
+ break
+
+ producer_thread = threading.Thread(target=data_producer)
+ producer_thread.start()
+
+ try:
+ while context.is_active():
+ try:
+ item = data_queue.get(timeout=30)
+ yield item
+ except queue.Empty:
+ # Send heartbeat or check client connection
+ if not context.is_active():
+ break
+ finally:
+ producer_thread.join(timeout=1)
+```
+
+### Error Handling in Streams
+Handle errors gracefully in streaming operations:
+
+```python title="stream_error_handling.py"
+def StreamWithErrorHandling(self, request, context):
+ """Stream with robust error handling."""
+
+ try:
+ for item in self.get_stream_data(request):
+ if not context.is_active():
+ break
+
+ try:
+ # Process item
+ processed_item = self.process_item(item)
+ yield processed_item
+
+ except ProcessingError as e:
+ # Send error as part of response
+ error_response = create_error_response(e)
+ yield error_response
+
+ except Exception as e:
+ # Critical error - abort stream
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Processing failed: {str(e)}')
+ break
+
+ except Exception as e:
+ context.set_code(grpc.StatusCode.INTERNAL)
+ context.set_details(f'Stream failed: {str(e)}')
+```
+
+### Client-side Streaming
+
+Handle streaming on the client side:
+
+```python title="streaming_client.py"
+import grpc
+
+def stream_chat_client():
+ """Example bidirectional streaming client."""
+
+ channel = grpc.insecure_channel('localhost:50051')
+ stub = streaming_pb2_grpc.StreamingServiceStub(channel)
+
+ def message_generator():
+ """Generate outgoing messages."""
+ # Send initial message
+ yield streaming_pb2.ChatMessage(
+ user_id="user123",
+ room_id="general",
+ content="Hello, world!",
+ type=streaming_pb2.CHAT_MESSAGE_TYPE_TEXT
+ )
+
+ # Keep connection alive and send periodic messages
+ while True:
+ user_input = input("Enter message: ")
+ if user_input.lower() == 'quit':
+ break
+
+ yield streaming_pb2.ChatMessage(
+ user_id="user123",
+ room_id="general",
+ content=user_input,
+ type=streaming_pb2.CHAT_MESSAGE_TYPE_TEXT
+ )
+
+ # Start bidirectional stream
+ responses = stub.Chat(message_generator())
+
+ try:
+ for response in responses:
+ print(f"Received: {response.content}")
+ except grpc.RpcError as e:
+ print(f"RPC failed: {e}")
+```
+
+Streaming in gRPC enables powerful real-time applications while maintaining the benefits of strongly-typed contracts and efficient binary protocols.
\ No newline at end of file
diff --git a/fern/products/openapi-def/openapi-def.yml b/fern/products/openapi-def/openapi-def.yml
index 638d1062e..419d75e5b 100644
--- a/fern/products/openapi-def/openapi-def.yml
+++ b/fern/products/openapi-def/openapi-def.yml
@@ -43,6 +43,5 @@ navigation:
slug: frameworks
contents:
- page: FastAPI
- icon: fa-regular fa-circle-bolt
path: ./pages/server-frameworks/fastapi.mdx
slug: fastapi
diff --git a/fern/products/openrpc-def/openrpc-def.yml b/fern/products/openrpc-def/openrpc-def.yml
index 07abf61a7..2235fb2a9 100644
--- a/fern/products/openrpc-def/openrpc-def.yml
+++ b/fern/products/openrpc-def/openrpc-def.yml
@@ -1,61 +1,39 @@
navigation:
- - section: Getting Started
+ - page: Overview
+ path: ./pages/overview.mdx
+ - page: Authentication
+ path: ./pages/auth.mdx
+ - page: Servers
+ path: ./pages/servers.mdx
+ - section: Methods
+ slug: methods
contents:
- - page: Overview
- path: ./pages/getting-started/overview.mdx
- - page: Get to OpenAPI
- path: ./pages/getting-started/get-to-openapi.mdx
- - page: Philosophy
- path: ./pages/getting-started/philosophy.mdx
- - page: Customer Showcase
- path: ./pages/getting-started/customer-showcase.mdx
- - section: Get to OpenAPI
- contents:
- - page: Handwrite it
- path: ./pages/getting-started/get-to-openapi/handwrite-it.mdx
- - section: Generate it
- contents:
- - page: FastAPI
- path: ./pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
- - page: NestJS
- path: ./pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
- - page: Swaggo
- path: ./pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
- - page: Request a new framework
- path: ./pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
- - page: Overlay customizations
- path: ./pages/getting-started/overlay-customizations.mdx
- - section: Security Schemes
+ - page: JSON-RPC Methods
+ path: ./pages/methods/rpc-methods.mdx
+ slug: rpc-methods
+ - page: Notifications
+ path: ./pages/methods/notifications.mdx
+ slug: notifications
+ - page: Batch Requests
+ path: ./pages/methods/batch.mdx
+ slug: batch
+ - section: Extensions
+ slug: extensions
contents:
- - page: Overview
- path: ./pages/security-schemes/overview.mdx
- - page: Overriding the security scheme
- path: ./pages/security-schemes/overriding-security-scheme.mdx
- - page: Multiple security schemes
- path: ./pages/security-schemes/multiple-security-schemes.mdx
- - section: Servers
+ - page: Audiences
+ path: ./pages/extensions/audiences.mdx
+ - page: SDK Method Names
+ path: ./pages/extensions/method-names.mdx
+ slug: method-names
+ - page: Parameter Names
+ path: ./pages/extensions/parameter-names.mdx
+ - page: Other
+ path: ./pages/extensions/others.mdx
+ slug: others
+ - section: Workflow & Automation
+ skip-slug: true
contents:
- - page: Overview
- path: ./pages/servers/overview.mdx
- - page: Overriding the servers
- path: ./pages/servers/overriding-servers.mdx
- - page: Naming your servers
- path: ./pages/servers/naming-your-servers.mdx
- - page: Multiple server URLs
- path: ./pages/servers/multiple-server-urls.mdx
- - section: Schemas
- contents:
- - page: Objects
- path: ./pages/schemas/objects.mdx
- - page: AllOf
- path: ./pages/schemas/allof.mdx
- - page: OneOf
- path: ./pages/schemas/oneof.mdx
- - page: AnyOf
- path: ./pages/schemas/anyof.mdx
- - page: Enums
- path: ./pages/schemas/enums.mdx
- - page: Override the type
- path: ./pages/schemas/override-type.mdx
- - page: Inlined schemas
- path: ./pages/schemas/inlined-schemas.mdx
+ - page: Overlay Customizations
+ path: ./pages/overrides.mdx
+ - page: Sync your OpenRPC Specification
+ path: ./pages/automation.mdx
diff --git a/fern/products/openrpc-def/pages/auth.mdx b/fern/products/openrpc-def/pages/auth.mdx
new file mode 100644
index 000000000..aefa3f0e6
--- /dev/null
+++ b/fern/products/openrpc-def/pages/auth.mdx
@@ -0,0 +1,235 @@
+---
+title: Authentication
+subtitle: Model auth schemes for JSON-RPC APIs including bearer, basic, and api key authentication
+---
+
+Authentication in OpenRPC can be configured at the server level or method level, depending on your JSON-RPC implementation. Unlike REST APIs, JSON-RPC typically handles authentication through the transport layer (HTTP headers) or within the JSON-RPC request payload.
+
+## HTTP Transport Authentication
+
+When using HTTP as the transport for JSON-RPC, you can use standard HTTP authentication schemes.
+
+### Bearer Token Authentication
+
+Configure bearer token authentication for HTTP-based JSON-RPC:
+
+```yml title="openrpc.yml" {4-9}
+servers:
+ - name: production
+ url: https://api.example.com/rpc
+ description: Production JSON-RPC server
+ security:
+ - bearerAuth: []
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
+```
+
+This generates SDK methods that require a token:
+
+```typescript
+const client = new JSONRPCClient({
+ url: "https://api.example.com/rpc",
+ auth: {
+ bearer: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ }
+});
+
+// Call JSON-RPC method
+const result = await client.call("calculate.add", { a: 5, b: 3 });
+```
+
+### API Key Authentication
+
+Configure API key authentication:
+
+```yml title="openrpc.yml" {4-9}
+servers:
+ - name: production
+ url: https://api.example.com/rpc
+ description: Production JSON-RPC server
+ security:
+ - apiKeyAuth: []
+components:
+ securitySchemes:
+ apiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+```
+
+Usage in SDK:
+
+```typescript
+const client = new JSONRPCClient({
+ url: "https://api.example.com/rpc",
+ auth: {
+ apiKey: "your-api-key-here"
+ }
+});
+```
+
+### Basic Authentication
+
+Configure basic authentication:
+
+```yml title="openrpc.yml" {4-9}
+servers:
+ - name: production
+ url: https://api.example.com/rpc
+ description: Production JSON-RPC server
+ security:
+ - basicAuth: []
+components:
+ securitySchemes:
+ basicAuth:
+ type: http
+ scheme: basic
+```
+
+Usage in SDK:
+
+```typescript
+const client = new JSONRPCClient({
+ url: "https://api.example.com/rpc",
+ auth: {
+ username: "user@example.com",
+ password: "password123"
+ }
+});
+```
+
+## Method-Level Authentication
+
+Some JSON-RPC implementations may require different authentication for specific methods:
+
+```yml title="openrpc.yml" {6-7, 15-16}
+methods:
+ - name: public.getInfo
+ summary: Get public information
+ description: Publicly accessible method (no auth required)
+ params: []
+ result:
+ name: info
+ schema:
+ type: object
+ - name: user.getProfile
+ summary: Get user profile
+ description: Requires user authentication
+ security:
+ - bearerAuth: []
+ params:
+ - name: userId
+ schema:
+ type: string
+ required: true
+ result:
+ name: profile
+ schema:
+ $ref: '#/components/schemas/UserProfile'
+```
+
+## WebSocket Authentication
+
+For WebSocket transport, authentication typically happens during connection establishment:
+
+```yml title="openrpc.yml" {4-8}
+servers:
+ - name: websocket
+ url: wss://api.example.com/rpc
+ description: WebSocket JSON-RPC server
+ variables:
+ token:
+ description: Authentication token for WebSocket connection
+ default: ""
+ security:
+ - wsAuth: []
+components:
+ securitySchemes:
+ wsAuth:
+ type: apiKey
+ in: query
+ name: token
+ description: Authentication token passed as query parameter
+```
+
+## Custom Authentication Parameters
+
+For JSON-RPC APIs that handle authentication within the request payload:
+
+```yml title="openrpc.yml" {8-16}
+methods:
+ - name: auth.login
+ summary: Authenticate user
+ description: Login method that returns authentication token
+ params:
+ - name: credentials
+ schema:
+ type: object
+ properties:
+ username:
+ type: string
+ password:
+ type: string
+ required:
+ - username
+ - password
+ result:
+ name: authResult
+ schema:
+ type: object
+ properties:
+ token:
+ type: string
+ expiresIn:
+ type: integer
+ refreshToken:
+ type: string
+```
+
+## Fern Extensions for Authentication
+
+Use Fern extensions to customize authentication behavior:
+
+```yml title="openrpc.yml" {5-8}
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ x-fern-token:
+ name: authToken
+ env: AUTH_TOKEN
+```
+
+This allows users to set authentication via environment variables or constructor parameters, making the SDK more flexible and secure.
+
+## Error Handling for Authentication
+
+Define standardized error responses for authentication failures:
+
+```yml title="openrpc.yml" {2-12}
+components:
+ errors:
+ - code: -32001
+ message: Authentication required
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Authentication token is required"
+ - code: -32002
+ message: Invalid authentication
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Invalid or expired authentication token"
+```
+
+These error codes follow JSON-RPC 2.0 conventions while providing clear authentication feedback to API consumers.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/automation.mdx b/fern/products/openrpc-def/pages/automation.mdx
new file mode 100644
index 000000000..b967c544a
--- /dev/null
+++ b/fern/products/openrpc-def/pages/automation.mdx
@@ -0,0 +1,316 @@
+---
+title: Sync your OpenRPC Specification
+subtitle: Automatically sync your OpenRPC spec changes to keep SDKs and docs up to date
+---
+
+Keeping your OpenRPC specifications in sync with your codebase is crucial for maintaining accurate SDKs and documentation. Fern provides several automation options to streamline this process.
+
+## GitHub Actions
+
+Use Fern's GitHub Action to automatically update SDKs and docs when your OpenRPC spec changes:
+
+```yaml title=".github/workflows/fern.yml"
+name: Fern
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ fern-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Check OpenRPC spec
+ uses: fern-api/action@v0
+ with:
+ command: check
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+
+ fern-generate:
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Generate SDKs and docs
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Webhook integration
+
+Set up webhooks to trigger SDK generation when your OpenRPC spec is updated:
+
+```yaml title="generators.yml" {4-8}
+api:
+ specs:
+ - spec: openrpc.yml
+ github:
+ repository: your-org/your-repo
+ webhooks:
+ - url: https://your-api.com/webhooks/fern
+ events: [generate]
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@your-org/jsonrpc-sdk"
+```
+
+## Auto-sync from source
+
+Configure Fern to automatically pull your OpenRPC specification from various sources:
+
+### From URL
+```yaml title="generators.yml" {3-4}
+api:
+ specs:
+ - spec: https://api.yourcompany.com/openrpc.yml
+ auto-sync: true
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+### From Git repository
+```yaml title="generators.yml" {3-7}
+api:
+ specs:
+ - spec:
+ git:
+ repository: https://github.com/your-org/api-specs
+ path: openrpc/api.yml
+ branch: main
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## CI/CD integration
+
+### CircleCI
+```yaml title=".circleci/config.yml" {15-23}
+version: 2.1
+
+orbs:
+ fern: fernapi/fern@1.0
+
+workflows:
+ version: 2
+ build-and-test:
+ jobs:
+ - build
+ - test:
+ requires:
+ - build
+ - fern/generate:
+ requires:
+ - test
+ filters:
+ branches:
+ only: main
+ context:
+ - fern-context
+```
+
+### GitLab CI
+```yaml title=".gitlab-ci.yml" {13-20}
+stages:
+ - build
+ - test
+ - generate
+
+variables:
+ FERN_TOKEN: $FERN_TOKEN
+
+build:
+ stage: build
+ script:
+ - echo "Building JSON-RPC service..."
+
+generate-sdks:
+ stage: generate
+ image: fernapi/fern:latest
+ script:
+ - fern generate
+ only:
+ - main
+```
+
+## Scheduled updates
+
+Set up scheduled updates to ensure your SDKs stay current:
+
+```yaml title=".github/workflows/scheduled-update.yml"
+name: Scheduled OpenRPC Update
+
+on:
+ schedule:
+ - cron: '0 2 * * 1' # Every Monday at 2 AM UTC
+ workflow_dispatch:
+
+jobs:
+ update-specs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Update OpenRPC specs
+ run: |
+ curl -o fern/openrpc/openrpc.yml https://api.yourcompany.com/openrpc.yml
+
+ - name: Generate with latest spec
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Code generation from JSON-RPC server
+
+For servers that can generate their own OpenRPC specifications:
+
+```yaml title=".github/workflows/auto-generate.yml"
+name: Auto-generate from JSON-RPC server
+
+on:
+ push:
+ paths:
+ - 'src/**/*.py' # Trigger on server code changes
+ - 'src/**/*.js'
+ - 'src/**/*.ts'
+
+jobs:
+ generate-spec:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup environment
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+
+ - name: Install dependencies
+ run: |
+ pip install -r requirements.txt
+
+ - name: Generate OpenRPC spec
+ run: |
+ python scripts/generate_openrpc.py --output fern/openrpc/openrpc.yml
+
+ - name: Generate SDKs
+ uses: fern-api/action@v0
+ with:
+ command: generate
+ env:
+ FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
+```
+
+## Monitoring changes
+
+Track changes to your OpenRPC specification:
+
+```yaml title="generators.yml" {4-9}
+api:
+ specs:
+ - spec: openrpc.yml
+ change-detection:
+ enabled: true
+ breaking-changes: error
+ notifications:
+ slack: ${{ secrets.SLACK_WEBHOOK }}
+ email: team@yourcompany.com
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Multi-environment sync
+
+Sync different specifications for different environments:
+
+```yaml title="generators.yml" {3-7, 12-16, 21-25}
+environments:
+ production:
+ specs:
+ - spec: https://api.prod.yourcompany.com/openrpc.yml
+ overlays:
+ - prod-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@yourcompany/prod-sdk"
+ staging:
+ specs:
+ - spec: https://api.staging.yourcompany.com/openrpc.yml
+ overlays:
+ - staging-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: npm
+ package-name: "@yourcompany/staging-sdk"
+ development:
+ specs:
+ - spec: http://localhost:8080/openrpc.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ output:
+ location: local
+ path: ./generated-sdk
+```
+
+## JSON-RPC server introspection
+
+For servers that support OpenRPC discovery:
+
+```python title="scripts/sync_from_server.py"
+import requests
+import yaml
+import json
+
+def fetch_openrpc_spec(server_url):
+ """Fetch OpenRPC spec from a JSON-RPC server that supports introspection"""
+ payload = {
+ "jsonrpc": "2.0",
+ "method": "rpc.discover",
+ "id": 1
+ }
+
+ response = requests.post(f"{server_url}/rpc", json=payload)
+ spec = response.json()["result"]
+
+ return spec
+
+def save_spec_as_yaml(spec, output_path):
+ """Convert JSON spec to YAML and save"""
+ with open(output_path, 'w') as f:
+ yaml.dump(spec, f, default_flow_style=False)
+
+if __name__ == "__main__":
+ spec = fetch_openrpc_spec("https://api.yourcompany.com")
+ save_spec_as_yaml(spec, "fern/openrpc/openrpc.yml")
+```
+
+This ensures that any breaking changes to your OpenRPC specification are detected and the appropriate team members are notified before the changes are propagated to your SDKs and documentation.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/extensions/audiences.mdx b/fern/products/openrpc-def/pages/extensions/audiences.mdx
new file mode 100644
index 000000000..6e7afb97d
--- /dev/null
+++ b/fern/products/openrpc-def/pages/extensions/audiences.mdx
@@ -0,0 +1,295 @@
+---
+title: Use audiences to filter your API
+subtitle: Use `x-fern-audiences` to filter to relevant methods, parameters and schemas
+---
+
+Audiences are a useful tool for segmenting your JSON-RPC API for different consumers. Common examples of audiences include `public`
+and `beta`.
+
+
+ Remember to filter your SDKs and Docs after specifying audiences. If **no audiences** are specified,
+ nothing will be filtered.
+
+
+
+
+The following example configures the SDK to filter to the `public` audience:
+
+```yaml title="generators.yml" {3-4}
+groups:
+ sdks:
+ audiences:
+ - public
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+
+
+The following example configures the docs to filter to the `public` audience:
+
+```yaml title="docs.yml" {3-4}
+navigation:
+ - api: API Reference
+ audiences:
+ - public
+```
+
+
+
+
+
+## Filter methods
+
+Add `x-fern-audiences` to methods to control which methods are included for specific audiences:
+
+```yaml title="openrpc.yml" {5-6, 12-13}
+methods:
+ - name: public.getInfo
+ summary: Get public API information
+ description: Publicly available information about the API
+ x-fern-audiences:
+ - public
+ params: []
+ result:
+ name: info
+ schema:
+ $ref: '#/components/schemas/ApiInfo'
+ - name: admin.getUsers
+ summary: Get all users (admin only)
+ x-fern-audiences:
+ - admin
+ params:
+ - name: limit
+ schema:
+ type: integer
+ default: 100
+ result:
+ name: users
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+```
+
+## Filter method parameters
+
+You can filter specific parameters within methods:
+
+```yaml title="openrpc.yml" {8-9, 14-15}
+methods:
+ - name: user.create
+ summary: Create a new user
+ params:
+ - name: userData
+ schema:
+ type: object
+ properties:
+ email:
+ type: string
+ format: email
+ name:
+ type: string
+ adminNotes:
+ type: string
+ x-fern-audiences:
+ - admin
+ internalId:
+ type: string
+ x-fern-audiences:
+ - internal
+ required:
+ - email
+ - name
+ required: true
+ result:
+ name: user
+ schema:
+ $ref: '#/components/schemas/User'
+```
+
+## Filter schemas
+
+Filter entire schemas to different audiences:
+
+```yaml title="openrpc.yml" {5-6, 20-21}
+components:
+ schemas:
+ PublicUser:
+ type: object
+ x-fern-audiences:
+ - public
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ email:
+ type: string
+ required:
+ - id
+ - name
+ - email
+ AdminUser:
+ allOf:
+ - $ref: '#/components/schemas/PublicUser'
+ - type: object
+ x-fern-audiences:
+ - admin
+ properties:
+ role:
+ type: string
+ permissions:
+ type: array
+ items:
+ type: string
+ createdAt:
+ type: string
+ format: date-time
+ lastLoginAt:
+ type: string
+ format: date-time
+```
+
+## Filter schema properties
+
+You can filter individual properties within schemas:
+
+```yaml title="openrpc.yml" {9-10, 13-14, 17-18}
+components:
+ schemas:
+ Order:
+ type: object
+ properties:
+ id:
+ type: string
+ amount:
+ type: number
+ x-fern-audiences:
+ - public
+ internalCost:
+ type: number
+ x-fern-audiences:
+ - internal
+ debugInfo:
+ type: object
+ x-fern-audiences:
+ - debug
+ customerInfo:
+ type: object
+ properties:
+ id:
+ type: string
+ email:
+ type: string
+ x-fern-audiences:
+ - admin
+```
+
+## Filter error responses
+
+Filter error information based on audience:
+
+```yaml title="openrpc.yml" {7-12, 14-19}
+methods:
+ - name: payment.process
+ summary: Process a payment
+ params:
+ - name: paymentData
+ schema:
+ $ref: '#/components/schemas/PaymentRequest'
+ errors:
+ - code: -32001
+ message: Payment failed
+ x-fern-audiences:
+ - public
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Payment could not be processed"
+ - code: -32001
+ message: Payment failed
+ x-fern-audiences:
+ - admin
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ errorCode:
+ type: string
+ gatewayResponse:
+ type: object
+ debugTrace:
+ type: string
+```
+
+## Server-level filtering
+
+Apply audience filtering at the server level:
+
+```yaml title="openrpc.yml" {4-5, 10-11}
+servers:
+ - name: public-api
+ url: https://api.example.com/rpc
+ x-fern-audiences:
+ - public
+ description: Public API server
+ - name: admin-api
+ url: https://admin-api.example.com/rpc
+ x-fern-audiences:
+ - admin
+ description: Admin API server with additional privileges
+```
+
+## Conditional method availability
+
+Use audiences to make methods available only in certain contexts:
+
+```yaml title="openrpc.yml" {5-6, 15-16, 25-26}
+methods:
+ - name: debug.getSystemInfo
+ summary: Get system debug information
+ description: Internal system information for debugging
+ x-fern-audiences:
+ - debug
+ params: []
+ result:
+ name: systemInfo
+ schema:
+ type: object
+ additionalProperties: true
+ - name: beta.advancedSearch
+ summary: Advanced search functionality
+ x-fern-audiences:
+ - beta
+ params:
+ - name: query
+ schema:
+ type: object
+ properties:
+ text:
+ type: string
+ filters:
+ type: object
+ additionalProperties: true
+ result:
+ name: results
+ schema:
+ $ref: '#/components/schemas/SearchResults'
+ - name: internal.resetCache
+ summary: Reset internal caches
+ x-fern-audiences:
+ - internal
+ params:
+ - name: cacheType
+ schema:
+ type: string
+ enum: [user, product, session, all]
+ # Notification - no result expected
+```
+
+This allows you to create different views of the same JSON-RPC API for different types of consumers, ensuring each audience only sees the methods and data relevant to their use case.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/extensions/method-names.mdx b/fern/products/openrpc-def/pages/extensions/method-names.mdx
new file mode 100644
index 000000000..d9e0528dd
--- /dev/null
+++ b/fern/products/openrpc-def/pages/extensions/method-names.mdx
@@ -0,0 +1,228 @@
+---
+title: SDK Method Names
+subtitle: Use `x-fern-sdk-method-name` to customize SDK method names for JSON-RPC methods
+---
+
+By default, Fern generates SDK method names based on your OpenRPC method names. You can override this behavior using the `x-fern-sdk-method-name` extension.
+
+## Customize method names
+
+Use `x-fern-sdk-method-name` to specify custom method names for your JSON-RPC methods:
+
+```yaml title="openrpc.yml" {4-5, 12-13}
+methods:
+ - name: user.getById
+ summary: Get user by ID
+ x-fern-sdk-method-name: getUser
+ params:
+ - name: id
+ schema:
+ type: string
+ required: true
+ result:
+ name: user
+ schema:
+ $ref: '#/components/schemas/User'
+ - name: order.createNew
+ summary: Create a new order
+ x-fern-sdk-method-name: create
+ params:
+ - name: orderData
+ schema:
+ $ref: '#/components/schemas/CreateOrderRequest'
+ required: true
+ result:
+ name: order
+ schema:
+ $ref: '#/components/schemas/Order'
+```
+
+This will generate SDK methods like:
+
+```typescript
+// Instead of client.user.getById()
+const user = await client.user.getUser({ id: "user_123" });
+
+// Instead of client.order.createNew()
+const order = await client.order.create({ orderData: {...} });
+```
+
+## Method naming conventions
+
+Follow these conventions when naming SDK methods:
+
+### CRUD operations
+Use standard CRUD naming:
+
+```yaml title="openrpc.yml" {4-5}
+methods:
+ - name: user.createUser
+ summary: Create a new user
+ x-fern-sdk-method-name: create
+ # Generates: client.user.create()
+
+ - name: user.getUserById
+ summary: Get user by ID
+ x-fern-sdk-method-name: get
+ # Generates: client.user.get()
+
+ - name: user.updateUser
+ summary: Update user information
+ x-fern-sdk-method-name: update
+ # Generates: client.user.update()
+
+ - name: user.deleteUser
+ summary: Delete a user
+ x-fern-sdk-method-name: delete
+ # Generates: client.user.delete()
+```
+
+### List operations
+Use descriptive names for list operations:
+
+```yaml title="openrpc.yml" {4-5}
+methods:
+ - name: user.getAllUsers
+ summary: Get all users
+ x-fern-sdk-method-name: list
+ # Generates: client.user.list()
+
+ - name: user.searchUsers
+ summary: Search for users
+ x-fern-sdk-method-name: search
+ # Generates: client.user.search()
+
+ - name: order.getUserOrders
+ summary: Get orders for a user
+ x-fern-sdk-method-name: listByUser
+ # Generates: client.order.listByUser()
+```
+
+### Action operations
+Use action-oriented names:
+
+```yaml title="openrpc.yml" {4-5}
+methods:
+ - name: email.sendNotification
+ summary: Send email notification
+ x-fern-sdk-method-name: send
+ # Generates: client.email.send()
+
+ - name: payment.processPayment
+ summary: Process a payment
+ x-fern-sdk-method-name: process
+ # Generates: client.payment.process()
+
+ - name: cache.invalidateCache
+ summary: Invalidate cache entries
+ x-fern-sdk-method-name: invalidate
+ # Generates: client.cache.invalidate()
+```
+
+## Language-specific method names
+
+You can specify different method names for different programming languages:
+
+```yaml title="openrpc.yml" {4-9}
+methods:
+ - name: user.getUserPreferences
+ summary: Get user preferences
+ x-fern-sdk-method-name:
+ python: get_preferences
+ typescript: getPreferences
+ go: GetPreferences
+ java: getPreferences
+ csharp: GetPreferences
+ params:
+ - name: userId
+ schema:
+ type: string
+ required: true
+ result:
+ name: preferences
+ schema:
+ $ref: '#/components/schemas/UserPreferences'
+```
+
+## Namespaced method names
+
+For methods with namespace prefixes, customize the final method name:
+
+```yaml title="openrpc.yml" {4-5, 12-13}
+methods:
+ - name: analytics.track.pageView
+ summary: Track page view event
+ x-fern-sdk-method-name: trackPageView
+ params:
+ - name: eventData
+ schema:
+ $ref: '#/components/schemas/PageViewEvent'
+ required: true
+ - name: analytics.track.conversion
+ summary: Track conversion event
+ x-fern-sdk-method-name: trackConversion
+ params:
+ - name: eventData
+ schema:
+ $ref: '#/components/schemas/ConversionEvent'
+ required: true
+```
+
+Generates:
+```typescript
+await client.analytics.trackPageView({ eventData: {...} });
+await client.analytics.trackConversion({ eventData: {...} });
+```
+
+## Notification method names
+
+For notification methods (one-way calls), use appropriate naming:
+
+```yaml title="openrpc.yml" {4-5, 12-13}
+methods:
+ - name: log.recordError
+ summary: Record an error event
+ x-fern-sdk-method-name: logError
+ params:
+ - name: errorData
+ schema:
+ $ref: '#/components/schemas/ErrorData'
+ required: true
+ # No result - this is a notification
+ - name: metrics.incrementCounter
+ summary: Increment a metric counter
+ x-fern-sdk-method-name: increment
+ params:
+ - name: metric
+ schema:
+ type: string
+ required: true
+ - name: value
+ schema:
+ type: number
+ default: 1
+ # No result - this is a notification
+```
+
+## Async method naming
+
+For methods that return promises or futures, consider async naming:
+
+```yaml title="openrpc.yml" {4-5}
+methods:
+ - name: report.generateReport
+ summary: Generate a report (long-running)
+ x-fern-sdk-method-name: generateAsync
+ params:
+ - name: reportConfig
+ schema:
+ $ref: '#/components/schemas/ReportConfig'
+ required: true
+ result:
+ name: jobId
+ schema:
+ type: string
+ description: Job ID for tracking report generation
+```
+
+This ensures method names follow the conventions of each target language while maintaining clear and intuitive APIs for developers.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/extensions/others.mdx b/fern/products/openrpc-def/pages/extensions/others.mdx
new file mode 100644
index 000000000..365ae0461
--- /dev/null
+++ b/fern/products/openrpc-def/pages/extensions/others.mdx
@@ -0,0 +1,302 @@
+---
+title: Other Extensions
+subtitle: Additional Fern extensions for OpenRPC specifications
+---
+
+Fern supports various extensions to enhance your OpenRPC specifications and improve the generated SDKs and documentation.
+
+## `x-fern-ignore`
+
+Use `x-fern-ignore` to exclude specific methods or schemas from SDK generation:
+
+```yaml title="openrpc.yml" {4-5, 12-13}
+methods:
+ - name: debug.internalMethod
+ summary: Internal debugging method
+ x-fern-ignore: true
+ params:
+ - name: debugData
+ schema:
+ type: object
+ result:
+ name: debugResult
+ schema:
+ type: object
+ - name: test.experimentalFeature
+ summary: Experimental feature (not ready for public use)
+ x-fern-ignore: true
+ params: []
+ result:
+ name: result
+ schema:
+ type: string
+```
+
+## `x-fern-examples`
+
+Provide additional examples for better SDK documentation:
+
+```yaml title="openrpc.yml" {8-25}
+methods:
+ - name: user.create
+ summary: Create a new user
+ params:
+ - name: userData
+ schema:
+ $ref: '#/components/schemas/CreateUserRequest'
+ required: true
+ x-fern-examples:
+ - name: StandardUser
+ description: Create a regular user account
+ params:
+ userData:
+ email: "john@example.com"
+ name: "John Doe"
+ role: "user"
+ result:
+ id: "user_123"
+ email: "john@example.com"
+ name: "John Doe"
+ role: "user"
+ createdAt: "2024-01-15T10:30:00Z"
+ - name: AdminUser
+ description: Create an admin user account
+ params:
+ userData:
+ email: "admin@example.com"
+ name: "Admin User"
+ role: "admin"
+ permissions: ["read", "write", "delete"]
+ result:
+ id: "user_456"
+ email: "admin@example.com"
+ name: "Admin User"
+ role: "admin"
+ createdAt: "2024-01-15T10:30:00Z"
+ result:
+ name: user
+ schema:
+ $ref: '#/components/schemas/User'
+```
+
+## `x-fern-pagination`
+
+Configure pagination for methods that return multiple results:
+
+```yaml title="openrpc.yml" {6-11}
+methods:
+ - name: user.list
+ summary: List users with pagination
+ params:
+ - name: pagination
+ schema:
+ type: object
+ properties:
+ cursor:
+ type: string
+ limit:
+ type: integer
+ default: 20
+ x-fern-pagination:
+ cursor: pagination.cursor
+ results: users
+ next_cursor: pagination.nextCursor
+ has_next_page: pagination.hasMore
+ result:
+ name: userList
+ schema:
+ type: object
+ properties:
+ users:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+ pagination:
+ type: object
+ properties:
+ nextCursor:
+ type: string
+ hasMore:
+ type: boolean
+```
+
+## `x-fern-retry`
+
+Configure retry behavior for methods:
+
+```yaml title="openrpc.yml" {6-10}
+methods:
+ - name: payment.process
+ summary: Process payment with retry logic
+ params:
+ - name: paymentData
+ schema:
+ $ref: '#/components/schemas/PaymentRequest'
+ x-fern-retry:
+ max_attempts: 3
+ exponential_backoff: true
+ initial_delay: 1000
+ max_delay: 30000
+ result:
+ name: paymentResult
+ schema:
+ $ref: '#/components/schemas/PaymentResult'
+```
+
+## `x-fern-timeout`
+
+Configure timeout settings for methods:
+
+```yaml title="openrpc.yml" {6-8}
+methods:
+ - name: report.generate
+ summary: Generate large report (may take time)
+ params:
+ - name: reportConfig
+ schema:
+ $ref: '#/components/schemas/ReportConfig'
+ x-fern-timeout:
+ seconds: 300
+ description: "Report generation can take up to 5 minutes"
+ result:
+ name: report
+ schema:
+ $ref: '#/components/schemas/Report'
+```
+
+## `x-fern-error-handling`
+
+Configure error handling for methods:
+
+```yaml title="openrpc.yml" {10-20}
+methods:
+ - name: order.create
+ summary: Create a new order
+ params:
+ - name: orderData
+ schema:
+ $ref: '#/components/schemas/CreateOrderRequest'
+ errors:
+ - code: -32001
+ message: Insufficient inventory
+ x-fern-error-handling:
+ error_name: InsufficientInventoryError
+ retry_after_seconds: 60
+ - code: -32002
+ message: Payment failed
+ x-fern-error-handling:
+ error_name: PaymentFailedError
+ user_friendly_message: "Payment could not be processed. Please check your payment information."
+ result:
+ name: order
+ schema:
+ $ref: '#/components/schemas/Order'
+```
+
+## `x-fern-availability`
+
+Mark features as available in specific SDK versions:
+
+```yaml title="openrpc.yml" {4-6}
+methods:
+ - name: beta.advancedSearch
+ summary: Advanced search functionality
+ x-fern-availability:
+ status: beta
+ message: "This feature is in beta and may change"
+ params:
+ - name: searchQuery
+ schema:
+ $ref: '#/components/schemas/AdvancedSearchQuery'
+ result:
+ name: searchResults
+ schema:
+ $ref: '#/components/schemas/SearchResults'
+```
+
+## `x-fern-streaming`
+
+Mark methods as streaming for appropriate SDK generation:
+
+```yaml title="openrpc.yml" {4-6}
+methods:
+ - name: logs.stream
+ summary: Stream log events
+ x-fern-streaming:
+ type: server_sent_events
+ termination: client_closes
+ params:
+ - name: filters
+ schema:
+ type: object
+ properties:
+ level:
+ type: string
+ enum: [debug, info, warn, error]
+ service:
+ type: string
+ # Note: Streaming methods typically don't have a traditional result
+```
+
+## `x-fern-server-name`
+
+Specify custom names for servers:
+
+```yaml title="openrpc.yml" {3-4}
+servers:
+ - name: production
+ url: https://api.yourcompany.com/rpc
+ x-fern-server-name: Production
+ description: Production JSON-RPC server
+ - name: staging
+ url: https://staging-api.yourcompany.com/rpc
+ x-fern-server-name: Staging
+ description: Staging environment
+```
+
+## `x-fern-base-path`
+
+Configure base paths for generated SDK clients:
+
+```yaml title="openrpc.yml" {3-4}
+info:
+ title: My JSON-RPC API
+ version: 1.0.0
+ x-fern-base-path: /api/v1/rpc
+ description: JSON-RPC API with custom base path
+```
+
+## `x-fern-sdk-group-name`
+
+Group related methods in the SDK:
+
+```yaml title="openrpc.yml" {4-5, 12-13}
+methods:
+ - name: user.create
+ summary: Create user
+ x-fern-sdk-group-name: users
+ params: [...]
+ result: {...}
+
+ - name: user.get
+ summary: Get user
+ x-fern-sdk-group-name: users
+ params: [...]
+ result: {...}
+
+ - name: order.create
+ summary: Create order
+ x-fern-sdk-group-name: orders
+ params: [...]
+ result: {...}
+```
+
+This generates SDKs with grouped methods:
+
+```typescript
+client.users.create(...)
+client.users.get(...)
+client.orders.create(...)
+```
+
+These extensions help you create more robust and user-friendly SDKs while maintaining full control over the generated code structure and behavior.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/extensions/parameter-names.mdx b/fern/products/openrpc-def/pages/extensions/parameter-names.mdx
new file mode 100644
index 000000000..d99aeedf1
--- /dev/null
+++ b/fern/products/openrpc-def/pages/extensions/parameter-names.mdx
@@ -0,0 +1,280 @@
+---
+title: Parameter Names
+subtitle: Use `x-fern-parameter-name` to customize SDK parameter names for JSON-RPC method parameters
+---
+
+By default, Fern uses the parameter names from your OpenRPC method definitions as SDK parameter names. You can customize these using the `x-fern-parameter-name` extension.
+
+## Customize parameter names
+
+Use `x-fern-parameter-name` to specify custom parameter names for method parameters:
+
+```yaml title="openrpc.yml" {8-9, 14-15}
+methods:
+ - name: user.create
+ summary: Create a new user
+ params:
+ - name: user_data
+ schema:
+ type: object
+ properties:
+ email_address:
+ type: string
+ format: email
+ x-fern-parameter-name: email
+ full_name:
+ type: string
+ x-fern-parameter-name: name
+ date_of_birth:
+ type: string
+ format: date
+ x-fern-parameter-name: dateOfBirth
+ required:
+ - email_address
+ - full_name
+ x-fern-parameter-name: userData
+ required: true
+ result:
+ name: user
+ schema:
+ $ref: '#/components/schemas/User'
+```
+
+This generates SDK methods with cleaner parameter names:
+
+```typescript
+// Instead of user_data with email_address, full_name, date_of_birth
+await client.user.create({
+ userData: {
+ email: "john@example.com",
+ name: "John Doe",
+ dateOfBirth: "1990-01-15"
+ }
+});
+```
+
+## Language-specific parameter names
+
+You can specify different parameter names for different programming languages:
+
+```yaml title="openrpc.yml" {8-13}
+methods:
+ - name: order.search
+ summary: Search for orders
+ params:
+ - name: search_criteria
+ schema:
+ type: object
+ properties:
+ customer_id:
+ type: string
+ x-fern-parameter-name:
+ python: customer_id
+ typescript: customerId
+ go: CustomerID
+ java: customerId
+ csharp: CustomerId
+ order_status:
+ type: string
+ enum: [pending, processing, shipped, delivered]
+ x-fern-parameter-name:
+ python: order_status
+ typescript: orderStatus
+ go: OrderStatus
+ java: orderStatus
+ csharp: OrderStatus
+ x-fern-parameter-name:
+ python: search_criteria
+ typescript: searchCriteria
+ go: SearchCriteria
+ java: searchCriteria
+ csharp: SearchCriteria
+ required: true
+ result:
+ name: orders
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Order'
+```
+
+## Nested parameter naming
+
+Customize parameter names for nested objects:
+
+```yaml title="openrpc.yml" {10-11, 16-17, 22-23}
+methods:
+ - name: payment.process
+ summary: Process a payment
+ params:
+ - name: payment_data
+ schema:
+ type: object
+ properties:
+ payment_method:
+ type: object
+ x-fern-parameter-name: paymentMethod
+ properties:
+ card_number:
+ type: string
+ x-fern-parameter-name: cardNumber
+ expiry_date:
+ type: string
+ x-fern-parameter-name: expiryDate
+ cvv:
+ type: string
+ billing_address:
+ type: object
+ x-fern-parameter-name: billingAddress
+ properties:
+ street_address:
+ type: string
+ x-fern-parameter-name: streetAddress
+ postal_code:
+ type: string
+ x-fern-parameter-name: postalCode
+ amount:
+ type: number
+ currency:
+ type: string
+ enum: [USD, EUR, GBP]
+ x-fern-parameter-name: paymentData
+ required: true
+ result:
+ name: paymentResult
+ schema:
+ $ref: '#/components/schemas/PaymentResult'
+```
+
+## Array parameter naming
+
+Handle array parameters with custom naming:
+
+```yaml title="openrpc.yml" {6-7, 14-15}
+methods:
+ - name: bulk.createUsers
+ summary: Create multiple users
+ params:
+ - name: user_list
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ email_addr:
+ type: string
+ format: email
+ x-fern-parameter-name: email
+ user_name:
+ type: string
+ x-fern-parameter-name: username
+ required:
+ - email_addr
+ - user_name
+ x-fern-parameter-name: users
+ minItems: 1
+ maxItems: 100
+ required: true
+ result:
+ name: createdUsers
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+```
+
+## Optional parameter naming
+
+Customize names for optional parameters with defaults:
+
+```yaml title="openrpc.yml" {8-9, 15-16, 22-23}
+methods:
+ - name: report.generate
+ summary: Generate a report
+ params:
+ - name: report_type
+ schema:
+ type: string
+ enum: [daily, weekly, monthly, yearly]
+ x-fern-parameter-name: reportType
+ required: true
+ - name: output_format
+ schema:
+ type: string
+ enum: [pdf, csv, json, excel]
+ default: pdf
+ x-fern-parameter-name: format
+ required: false
+ description: Output format (defaults to PDF)
+ - name: include_charts
+ schema:
+ type: boolean
+ default: true
+ x-fern-parameter-name: includeCharts
+ required: false
+ description: Include charts in the report
+ - name: date_range
+ schema:
+ type: object
+ properties:
+ start_date:
+ type: string
+ format: date
+ x-fern-parameter-name: startDate
+ end_date:
+ type: string
+ format: date
+ x-fern-parameter-name: endDate
+ x-fern-parameter-name: dateRange
+ required: false
+ description: Optional date range filter
+ result:
+ name: report
+ schema:
+ $ref: '#/components/schemas/Report'
+```
+
+## Positional parameter naming
+
+For methods using positional parameters, customize the generated parameter names:
+
+```yaml title="openrpc.yml" {4-6, 8-10}
+methods:
+ - name: math.calculate
+ summary: Perform mathematical calculation
+ paramStructure: by-position
+ params:
+ - schema:
+ type: number
+ description: First operand
+ x-fern-parameter-name: operandA
+ - schema:
+ type: number
+ description: Second operand
+ x-fern-parameter-name: operandB
+ - schema:
+ type: string
+ enum: [add, subtract, multiply, divide]
+ description: Operation to perform
+ x-fern-parameter-name: operation
+ result:
+ name: result
+ schema:
+ type: number
+```
+
+This generates cleaner SDK interfaces:
+
+```typescript
+// Instead of unnamed positional parameters
+await client.math.calculate(10, 5, "add");
+
+// With named parameters
+await client.math.calculate({
+ operandA: 10,
+ operandB: 5,
+ operation: "add"
+});
+```
+
+Using custom parameter names ensures consistent naming conventions across your SDK while maintaining compatibility with your existing JSON-RPC method signatures.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/customer-showcase.mdx b/fern/products/openrpc-def/pages/getting-started/customer-showcase.mdx
deleted file mode 100644
index 673a30502..000000000
--- a/fern/products/openrpc-def/pages/getting-started/customer-showcase.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Customer Showcase
----
-
-Examples and case studies of customers using OpenRPC definitions successfully.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi.mdx
deleted file mode 100644
index 284e44bb5..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Get to OpenAPI
----
-
-How to create or transition to OpenRPC definitions for your JSON-RPC APIs.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
deleted file mode 100644
index c837542d4..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/fastapi.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: FastAPI
----
-
-Instructions for generating an OpenRPC definition from a FastAPI project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
deleted file mode 100644
index c5175ab66..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/nestjs.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: NestJS
----
-
-Instructions for generating an OpenRPC definition from a NestJS project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
deleted file mode 100644
index c8477ff98..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/request-new-framework.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Request New Framework
----
-
-Don't see your framework? Let us know which framework you'd like to see supported for OpenRPC generation.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
deleted file mode 100644
index ac627d4f5..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/generate-it/swaggo.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Swaggo
----
-
-Instructions for generating an OpenRPC definition from a Swaggo (Go) project.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx b/fern/products/openrpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
deleted file mode 100644
index 4e127a434..000000000
--- a/fern/products/openrpc-def/pages/getting-started/get-to-openapi/handwrite-it.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Handwrite It
----
-
-Step-by-step guide to manually writing your OpenRPC definition from scratch.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/overlay-customizations.mdx b/fern/products/openrpc-def/pages/getting-started/overlay-customizations.mdx
deleted file mode 100644
index 6fb9aad86..000000000
--- a/fern/products/openrpc-def/pages/getting-started/overlay-customizations.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overlay Customizations
----
-
-Learn how to customize your OpenAPI overlays to fit your unique requirements.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/overview.mdx b/fern/products/openrpc-def/pages/getting-started/overview.mdx
deleted file mode 100644
index d6bf880f5..000000000
--- a/fern/products/openrpc-def/pages/getting-started/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overview
----
-
-This is the overview page for Getting Started with your OpenRPC definition. Here you will find a high-level introduction and guidance on how to begin.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/getting-started/philosophy.mdx b/fern/products/openrpc-def/pages/getting-started/philosophy.mdx
deleted file mode 100644
index 1e97a0d39..000000000
--- a/fern/products/openrpc-def/pages/getting-started/philosophy.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Philosophy
----
-
-The philosophy and principles behind OpenRPC definitions and how they enhance API development.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/methods/batch.mdx b/fern/products/openrpc-def/pages/methods/batch.mdx
new file mode 100644
index 000000000..3ec3720d4
--- /dev/null
+++ b/fern/products/openrpc-def/pages/methods/batch.mdx
@@ -0,0 +1,301 @@
+---
+title: Batch Requests
+subtitle: Document how to handle multiple JSON-RPC calls in a single request
+---
+
+Batch requests in JSON-RPC allow clients to send multiple method calls in a single HTTP request, improving efficiency and reducing network overhead. OpenRPC supports documenting batch request capabilities.
+
+## Batch request structure
+
+While individual methods are documented separately, batch requests combine multiple calls:
+
+```json title="Batch Request Example"
+[
+ {
+ "jsonrpc": "2.0",
+ "method": "user.get",
+ "params": { "id": "user_123" },
+ "id": 1
+ },
+ {
+ "jsonrpc": "2.0",
+ "method": "order.list",
+ "params": { "userId": "user_123", "limit": 10 },
+ "id": 2
+ },
+ {
+ "jsonrpc": "2.0",
+ "method": "analytics.track",
+ "params": {
+ "event": {
+ "userId": "user_123",
+ "eventType": "api_call",
+ "properties": { "endpoint": "batch_request" }
+ }
+ }
+ }
+]
+```
+
+## Documenting batch-capable methods
+
+Mark methods that work well in batch scenarios:
+
+```yml title="openrpc.yml" {4-5, 14-15}
+methods:
+ - name: user.get
+ summary: Get user by ID
+ x-fern-batch-compatible: true
+ x-fern-batch-description: "Efficiently retrieve multiple users in a single batch"
+ params:
+ - name: id
+ schema:
+ type: string
+ required: true
+ result:
+ name: user
+ schema:
+ $ref: '#/components/schemas/User'
+ - name: product.get
+ summary: Get product by ID
+ x-fern-batch-compatible: true
+ x-fern-batch-description: "Retrieve multiple products with a single request"
+ params:
+ - name: id
+ schema:
+ type: string
+ required: true
+ result:
+ name: product
+ schema:
+ $ref: '#/components/schemas/Product'
+```
+
+## Batch response handling
+
+Document how batch responses are structured:
+
+```yml title="openrpc.yml" {6-12}
+info:
+ title: E-commerce API
+ version: 1.0.0
+ description: |
+ This API supports JSON-RPC batch requests for improved performance.
+
+ Batch responses maintain the same order as requests and include:
+ - Successful results with matching request IDs
+ - Error responses for failed calls
+ - No response for notification calls (calls without IDs)
+
+ Example batch response:
+ ```json
+ [
+ {
+ "jsonrpc": "2.0",
+ "result": { "id": "user_123", "name": "John Doe" },
+ "id": 1
+ },
+ {
+ "jsonrpc": "2.0",
+ "result": [{ "id": "order_456", "total": 99.99 }],
+ "id": 2
+ }
+ ]
+ ```
+```
+
+## Mixed batch requests
+
+Document scenarios with regular methods and notifications:
+
+```yml title="openrpc.yml" {2-23}
+methods:
+ - name: order.create
+ summary: Create a new order
+ description: Can be used in batch requests with order.calculate for atomic operations
+ x-fern-batch-compatible: true
+ params:
+ - name: orderData
+ schema:
+ $ref: '#/components/schemas/CreateOrderRequest'
+ required: true
+ result:
+ name: order
+ schema:
+ $ref: '#/components/schemas/Order'
+ - name: order.calculate
+ summary: Calculate order totals
+ description: Often used before order.create in batch requests
+ x-fern-batch-compatible: true
+ params:
+ - name: items
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrderItem'
+ required: true
+ result:
+ name: calculation
+ schema:
+ $ref: '#/components/schemas/OrderCalculation'
+ examples:
+ - name: BatchOrderProcessing
+ description: Calculate and create order in a single batch
+ params:
+ items:
+ - productId: "prod_123"
+ quantity: 2
+ price: 29.99
+ - productId: "prod_456"
+ quantity: 1
+ price: 15.50
+ result:
+ subtotal: 75.48
+ tax: 6.04
+ shipping: 5.99
+ total: 87.51
+```
+
+## Error handling in batches
+
+Document how errors are handled in batch scenarios:
+
+```yml title="openrpc.yml" {6-15}
+info:
+ title: API Documentation
+ version: 1.0.0
+ description: |
+ ## Batch Error Handling
+
+ When processing batch requests:
+ - Each call is processed independently
+ - Successful calls return normal results
+ - Failed calls return error objects
+ - Notifications (calls without ID) produce no response
+ - Partial failures are allowed - some calls can succeed while others fail
+
+ Error response example:
+ ```json
+ {
+ "jsonrpc": "2.0",
+ "error": {
+ "code": -32602,
+ "message": "Invalid params",
+ "data": { "field": "email", "error": "Invalid email format" }
+ },
+ "id": 3
+ }
+ ```
+```
+
+## Batch optimization methods
+
+Document methods specifically designed for batch operations:
+
+```yml title="openrpc.yml" {2-19}
+methods:
+ - name: users.getBatch
+ summary: Get multiple users by IDs
+ description: Optimized method for retrieving multiple users in a single call
+ params:
+ - name: userIds
+ schema:
+ type: array
+ items:
+ type: string
+ maxItems: 100
+ minItems: 1
+ required: true
+ description: Array of user IDs to retrieve (max 100)
+ result:
+ name: users
+ schema:
+ type: object
+ properties:
+ users:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+ notFound:
+ type: array
+ items:
+ type: string
+ description: IDs that were not found
+ required:
+ - users
+ - notFound
+ examples:
+ - name: GetMultipleUsers
+ description: Retrieve several users at once
+ params:
+ userIds: ["user_123", "user_456", "user_789"]
+ result:
+ users:
+ - id: "user_123"
+ name: "John Doe"
+ email: "john@example.com"
+ - id: "user_456"
+ name: "Jane Smith"
+ email: "jane@example.com"
+ notFound: ["user_789"]
+```
+
+## Batch size limitations
+
+Document batch request limits and recommendations:
+
+```yml title="openrpc.yml" {4-8}
+info:
+ title: API with Batch Support
+ version: 1.0.0
+ x-fern-batch-limits:
+ maxBatchSize: 50
+ maxPayloadSize: "1MB"
+ timeoutSeconds: 30
+ description: "Batch requests are limited to 50 operations or 1MB payload size"
+ description: |
+ ## Batch Request Guidelines
+
+ - Maximum 50 operations per batch request
+ - Total payload size limited to 1MB
+ - Batch processing timeout: 30 seconds
+ - Use batch requests for:
+ - Retrieving related data in a single round-trip
+ - Performing multiple updates atomically
+ - Reducing network overhead for mobile applications
+
+ - Avoid batch requests for:
+ - Long-running operations (use individual calls)
+ - Operations that don't benefit from batching
+ - Calls that might exceed timeout limits
+```
+
+## SDK batch support
+
+Configure how SDKs handle batch requests:
+
+```yml title="openrpc.yml" {5-8}
+components:
+ x-fern-batch-config:
+ enableBatchMethods: true
+ generateBatchHelpers: true
+ batchMethodNaming: "batch{MethodName}"
+ maxBatchSize: 50
+```
+
+This configuration would generate SDK methods like:
+
+```typescript
+// Individual calls
+const user = await client.user.get({ id: "user_123" });
+const product = await client.product.get({ id: "prod_456" });
+
+// Batch call
+const results = await client.batch([
+ { method: "user.get", params: { id: "user_123" } },
+ { method: "product.get", params: { id: "prod_456" } },
+ { method: "analytics.track", params: { event: {...} } } // notification
+]);
+```
+
+Batch requests are essential for high-performance applications that need to minimize network round-trips while maintaining the flexibility of individual method calls.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/methods/notifications.mdx b/fern/products/openrpc-def/pages/methods/notifications.mdx
new file mode 100644
index 000000000..56ca7bdbf
--- /dev/null
+++ b/fern/products/openrpc-def/pages/methods/notifications.mdx
@@ -0,0 +1,309 @@
+---
+title: Notifications
+subtitle: Document one-way JSON-RPC calls that don't expect a response
+---
+
+Notifications in JSON-RPC are one-way method calls that don't expect a response from the server. They're useful for fire-and-forget operations like logging, analytics, or triggering background processes.
+
+## Basic notifications
+
+Define a notification by omitting the `result` property:
+
+```yml title="openrpc.yml" {2-12}
+methods:
+ - name: analytics.track
+ summary: Track user analytics event
+ description: Send an analytics event (no response expected)
+ params:
+ - name: event
+ schema:
+ type: object
+ properties:
+ userId:
+ type: string
+ eventType:
+ type: string
+ enum: [page_view, click, conversion, signup]
+ properties:
+ type: object
+ additionalProperties: true
+ timestamp:
+ type: string
+ format: date-time
+ required:
+ - userId
+ - eventType
+ required: true
+ # Note: No result property - this makes it a notification
+ examples:
+ - name: PageViewEvent
+ description: Track a page view
+ params:
+ event:
+ userId: "user_123"
+ eventType: "page_view"
+ properties:
+ page: "/dashboard"
+ referrer: "https://google.com"
+ timestamp: "2024-01-15T10:30:00Z"
+```
+
+## Logging notifications
+
+Use notifications for logging and monitoring:
+
+```yml title="openrpc.yml" {2-15}
+methods:
+ - name: log.error
+ summary: Log an error event
+ description: Send error information for monitoring (fire and forget)
+ params:
+ - name: errorData
+ schema:
+ type: object
+ properties:
+ level:
+ type: string
+ enum: [error, warning, info, debug]
+ message:
+ type: string
+ stackTrace:
+ type: string
+ userId:
+ type: string
+ sessionId:
+ type: string
+ metadata:
+ type: object
+ additionalProperties: true
+ required:
+ - level
+ - message
+ required: true
+ examples:
+ - name: ApplicationError
+ description: Log an application error
+ params:
+ errorData:
+ level: "error"
+ message: "Database connection failed"
+ stackTrace: "Error: Connection timeout\n at Database.connect (db.js:45)"
+ userId: "user_456"
+ sessionId: "session_789"
+ metadata:
+ component: "user-service"
+ version: "1.2.3"
+```
+
+## Real-time updates
+
+Send real-time updates without expecting responses:
+
+```yml title="openrpc.yml" {2-16}
+methods:
+ - name: presence.update
+ summary: Update user presence status
+ description: Notify about user presence changes
+ params:
+ - name: presenceData
+ schema:
+ type: object
+ properties:
+ userId:
+ type: string
+ status:
+ type: string
+ enum: [online, away, busy, offline]
+ lastSeen:
+ type: string
+ format: date-time
+ customMessage:
+ type: string
+ maxLength: 100
+ required:
+ - userId
+ - status
+ required: true
+ examples:
+ - name: UserOnline
+ description: User comes online
+ params:
+ presenceData:
+ userId: "user_123"
+ status: "online"
+ lastSeen: "2024-01-15T10:30:00Z"
+ customMessage: "Working on the new feature"
+```
+
+## Background processing
+
+Trigger background tasks without waiting for completion:
+
+```yml title="openrpc.yml" {2-20}
+methods:
+ - name: email.send
+ summary: Send email notification
+ description: Queue an email for sending (background processing)
+ params:
+ - name: emailRequest
+ schema:
+ type: object
+ properties:
+ to:
+ type: array
+ items:
+ type: string
+ format: email
+ minItems: 1
+ subject:
+ type: string
+ body:
+ type: string
+ templateId:
+ type: string
+ variables:
+ type: object
+ additionalProperties: true
+ priority:
+ type: string
+ enum: [low, normal, high, urgent]
+ default: normal
+ required:
+ - to
+ - subject
+ - body
+ required: true
+ examples:
+ - name: WelcomeEmail
+ description: Send welcome email to new user
+ params:
+ emailRequest:
+ to: ["newuser@example.com"]
+ subject: "Welcome to our platform!"
+ body: "Thank you for signing up..."
+ templateId: "welcome_template"
+ variables:
+ userName: "John Doe"
+ activationLink: "https://app.example.com/activate/123"
+ priority: "normal"
+```
+
+## Cache invalidation
+
+Notify about cache invalidation events:
+
+```yml title="openrpc.yml" {2-15}
+methods:
+ - name: cache.invalidate
+ summary: Invalidate cache entries
+ description: Notify cache system to invalidate specific entries
+ params:
+ - name: invalidationRequest
+ schema:
+ type: object
+ properties:
+ keys:
+ type: array
+ items:
+ type: string
+ description: Cache keys to invalidate
+ pattern:
+ type: string
+ description: Pattern for batch invalidation
+ namespace:
+ type: string
+ description: Cache namespace
+ anyOf:
+ - required: [keys]
+ - required: [pattern]
+ required: true
+ examples:
+ - name: InvalidateUserCache
+ description: Invalidate user-related cache entries
+ params:
+ invalidationRequest:
+ keys: ["user:123", "user:123:profile", "user:123:settings"]
+ namespace: "users"
+ - name: InvalidatePatternCache
+ description: Invalidate all product cache entries
+ params:
+ invalidationRequest:
+ pattern: "product:*"
+ namespace: "catalog"
+```
+
+## Audit logging
+
+Record audit events for compliance:
+
+```yml title="openrpc.yml" {2-18}
+methods:
+ - name: audit.record
+ summary: Record audit event
+ description: Log security and compliance events
+ params:
+ - name: auditEvent
+ schema:
+ type: object
+ properties:
+ eventType:
+ type: string
+ enum: [login, logout, data_access, data_modification, permission_change]
+ actor:
+ type: object
+ properties:
+ userId:
+ type: string
+ email:
+ type: string
+ ipAddress:
+ type: string
+ userAgent:
+ type: string
+ required: [userId]
+ resource:
+ type: object
+ properties:
+ type:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ timestamp:
+ type: string
+ format: date-time
+ metadata:
+ type: object
+ additionalProperties: true
+ required:
+ - eventType
+ - actor
+ - timestamp
+ required: true
+ examples:
+ - name: LoginAudit
+ description: Record user login event
+ params:
+ auditEvent:
+ eventType: "login"
+ actor:
+ userId: "user_123"
+ email: "john@example.com"
+ ipAddress: "192.168.1.100"
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ timestamp: "2024-01-15T10:30:00Z"
+ metadata:
+ loginMethod: "password"
+ mfaUsed: true
+```
+
+## Notification best practices
+
+- Use notifications for fire-and-forget operations where you don't need to know if the operation succeeded
+- Consider using regular methods with results for critical operations that need confirmation
+- Include enough context in notification parameters for proper processing
+- Use structured schemas even for notifications to maintain API consistency
+- Consider implementing client-side queuing for notifications in unreliable network conditions
+
+Notifications are perfect for decoupling systems and improving performance by not waiting for non-critical operations to complete.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/methods/rpc-methods.mdx b/fern/products/openrpc-def/pages/methods/rpc-methods.mdx
new file mode 100644
index 000000000..3f655553a
--- /dev/null
+++ b/fern/products/openrpc-def/pages/methods/rpc-methods.mdx
@@ -0,0 +1,277 @@
+---
+title: JSON-RPC Methods
+subtitle: Document JSON-RPC methods with parameters, results, and error handling
+---
+
+Methods in OpenRPC are the core building blocks of your JSON-RPC API. Each method defines the procedure name, parameters, return values, and potential errors.
+
+```yml title="openrpc.yml" {2-19}
+methods:
+ - name: user.create
+ summary: Create a new user
+ description: Creates a new user account with the provided information
+ params:
+ - name: userData
+ schema:
+ $ref: '#/components/schemas/CreateUserRequest'
+ required: true
+ description: User data for account creation
+ result:
+ name: createdUser
+ schema:
+ $ref: '#/components/schemas/User'
+ description: The newly created user object
+ examples:
+ - name: CreateUserExample
+ description: Example of creating a user
+ params:
+ userData:
+ email: "john@example.com"
+ name: "John Doe"
+ age: 30
+ result:
+ id: "user_123"
+ email: "john@example.com"
+ name: "John Doe"
+ age: 30
+ createdAt: "2024-01-15T10:30:00Z"
+```
+
+## Method parameters
+
+Parameters in JSON-RPC can be positional (by-position) or named (by-name):
+
+### Named parameters
+```yml title="openrpc.yml" {4-14}
+methods:
+ - name: calculate.add
+ summary: Add two numbers
+ paramStructure: by-name
+ params:
+ - name: a
+ schema:
+ type: number
+ required: true
+ description: First number
+ - name: b
+ schema:
+ type: number
+ required: true
+ description: Second number
+ result:
+ name: sum
+ schema:
+ type: number
+```
+
+### Positional parameters
+```yml title="openrpc.yml" {4-12}
+methods:
+ - name: calculate.multiply
+ summary: Multiply two numbers
+ paramStructure: by-position
+ params:
+ - schema:
+ type: number
+ description: First number (multiplicand)
+ - schema:
+ type: number
+ description: Second number (multiplier)
+ result:
+ name: product
+ schema:
+ type: number
+```
+
+## Complex parameter types
+
+Use complex schemas for method parameters:
+
+```yml title="openrpc.yml" {6-24}
+methods:
+ - name: order.create
+ summary: Create a new order
+ description: Creates a new order with items and shipping information
+ params:
+ - name: orderData
+ schema:
+ type: object
+ properties:
+ items:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrderItem'
+ minItems: 1
+ shippingAddress:
+ $ref: '#/components/schemas/Address'
+ paymentMethod:
+ type: string
+ enum: [credit_card, paypal, bank_transfer]
+ notes:
+ type: string
+ maxLength: 500
+ required:
+ - items
+ - shippingAddress
+ - paymentMethod
+ required: true
+ result:
+ name: order
+ schema:
+ $ref: '#/components/schemas/Order'
+```
+
+## Method results
+
+Define the structure of successful method responses:
+
+```yml title="openrpc.yml" {8-20}
+methods:
+ - name: search.products
+ summary: Search for products
+ params:
+ - name: query
+ schema:
+ type: string
+ required: true
+ result:
+ name: searchResults
+ schema:
+ type: object
+ properties:
+ products:
+ type: array
+ items:
+ $ref: '#/components/schemas/Product'
+ totalCount:
+ type: integer
+ hasMore:
+ type: boolean
+ nextCursor:
+ type: string
+ required:
+ - products
+ - totalCount
+ - hasMore
+```
+
+## Error handling
+
+Define custom errors for your methods:
+
+```yml title="openrpc.yml" {10-24}
+methods:
+ - name: user.login
+ summary: Authenticate user
+ params:
+ - name: email
+ schema:
+ type: string
+ format: email
+ required: true
+ errors:
+ - code: -32001
+ message: Invalid credentials
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Email or password is incorrect"
+ - code: -32002
+ message: Account locked
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Account temporarily locked due to failed login attempts"
+ unlockTime:
+ type: string
+ format: date-time
+ result:
+ name: authResult
+ schema:
+ $ref: '#/components/schemas/AuthToken'
+```
+
+## Method examples
+
+Provide comprehensive examples for better documentation:
+
+```yml title="openrpc.yml" {12-35}
+methods:
+ - name: file.upload
+ summary: Upload a file
+ params:
+ - name: fileData
+ schema:
+ type: object
+ properties:
+ filename:
+ type: string
+ content:
+ type: string
+ format: base64
+ mimeType:
+ type: string
+ required: true
+ examples:
+ - name: ImageUpload
+ description: Upload a JPEG image
+ params:
+ fileData:
+ filename: "photo.jpg"
+ content: "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC/gA=="
+ mimeType: "image/jpeg"
+ result:
+ fileId: "file_abc123"
+ url: "https://cdn.example.com/files/file_abc123.jpg"
+ size: 1024000
+ - name: DocumentUpload
+ description: Upload a PDF document
+ params:
+ fileData:
+ filename: "document.pdf"
+ content: "JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCgoyIDAgb2JqCjw8Ci9UeXBlIC9QYWdlcwo="
+ mimeType: "application/pdf"
+ result:
+ fileId: "file_def456"
+ url: "https://cdn.example.com/files/file_def456.pdf"
+ size: 256000
+```
+
+## Optional parameters
+
+Define optional parameters with default values:
+
+```yml title="openrpc.yml" {6-19}
+methods:
+ - name: report.generate
+ summary: Generate a report
+ params:
+ - name: reportType
+ schema:
+ type: string
+ enum: [daily, weekly, monthly, yearly]
+ required: true
+ - name: format
+ schema:
+ type: string
+ enum: [pdf, csv, json]
+ default: pdf
+ required: false
+ description: Output format (defaults to PDF)
+ - name: includeCharts
+ schema:
+ type: boolean
+ default: true
+ required: false
+ description: Include charts in the report
+ result:
+ name: report
+ schema:
+ $ref: '#/components/schemas/Report'
+```
+
+Methods are the foundation of your JSON-RPC API, providing a clear contract for what operations are available, what data they expect, and what they return.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/overrides.mdx b/fern/products/openrpc-def/pages/overrides.mdx
new file mode 100644
index 000000000..a3ae23e57
--- /dev/null
+++ b/fern/products/openrpc-def/pages/overrides.mdx
@@ -0,0 +1,274 @@
+---
+title: Overlay Customizations
+subtitle: Use overlay files to modify your OpenRPC specification without editing the original
+---
+
+Overlays allow you to modify your OpenRPC specification without directly editing the original file. This is useful for:
+- Adding Fern-specific extensions
+- Customizing documentation
+- Adding examples and descriptions
+- Overriding specific properties
+
+## Configure overlays
+
+To use overlays, add them to your `generators.yml` file:
+
+```yaml title="generators.yml" {3-5}
+api:
+ specs:
+ - spec: openrpc.yml
+ overlays:
+ - overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+## Overlay file structure
+
+Overlay files follow the [OpenAPI Overlay Specification](https://spec.openapis.org/overlay/v1.0.0) format:
+
+```yaml title="overlay.yml"
+overlay: 1.0.0
+info:
+ title: OpenRPC Fern Extensions
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'user.create')]
+ update:
+ x-fern-sdk-method-name: create
+ summary: Create a new user account
+ - target: $.methods[?(@.name == 'order.list')]
+ update:
+ description: Retrieve orders with optional filtering and pagination
+ x-fern-audiences:
+ - public
+```
+
+## Add method names
+
+Override SDK method names for better developer experience:
+
+```yaml title="overlay.yml" {7-9}
+overlay: 1.0.0
+info:
+ title: SDK Method Names
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'user.getById')]
+ update:
+ x-fern-sdk-method-name: getUser
+ summary: Retrieve user by ID with enhanced details
+ - target: $.methods[?(@.name == 'order.createNew')]
+ update:
+ x-fern-sdk-method-name: create
+ summary: Create order with automatic validation
+```
+
+## Add examples
+
+Enhance your specification with examples:
+
+```yaml title="overlay.yml" {7-20}
+overlay: 1.0.0
+info:
+ title: Method Examples
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'user.create')]
+ update:
+ examples:
+ - name: StandardUser
+ description: Create a regular user
+ params:
+ userData:
+ email: "john@example.com"
+ name: "John Doe"
+ role: "user"
+ result:
+ id: "user_123"
+ email: "john@example.com"
+ name: "John Doe"
+ createdAt: "2024-01-15T10:30:00Z"
+```
+
+## Filter with audiences
+
+Add audience filtering to methods:
+
+```yaml title="overlay.yml" {7-9, 12-14}
+overlay: 1.0.0
+info:
+ title: Audience Filtering
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'admin.getUsers')]
+ update:
+ x-fern-audiences:
+ - admin
+ - target: $.methods[?(@.name == 'debug.getSystemInfo')]
+ update:
+ x-fern-audiences:
+ - internal
+```
+
+## Add documentation
+
+Enhance descriptions and documentation:
+
+```yaml title="overlay.yml" {7-14}
+overlay: 1.0.0
+info:
+ title: Enhanced Documentation
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'payment.process')]
+ update:
+ description: |
+ Process a payment transaction with comprehensive validation and fraud detection.
+
+ This method supports multiple payment methods including credit cards,
+ digital wallets, and bank transfers. All transactions are processed
+ securely with PCI DSS compliance.
+
+ Returns a payment result with transaction details or error information.
+ summary: Process payment with fraud detection
+```
+
+## Server configurations
+
+Add server-specific configurations:
+
+```yaml title="overlay.yml" {7-12}
+overlay: 1.0.0
+info:
+ title: Server Extensions
+ version: 1.0.0
+actions:
+ - target: $.servers[?(@.name == 'production')]
+ update:
+ x-fern-server-name: Production
+ description: Production environment with high availability and monitoring
+ variables:
+ region:
+ default: us-east-1
+ enum: [us-east-1, us-west-2, eu-west-1]
+ description: AWS region for the API server
+```
+
+## Add parameter customizations
+
+Customize parameter names and descriptions:
+
+```yaml title="overlay.yml" {7-18}
+overlay: 1.0.0
+info:
+ title: Parameter Customizations
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'user.search')].params[?(@.name == 'search_criteria')]
+ update:
+ x-fern-parameter-name: searchCriteria
+ description: |
+ Search criteria for finding users. Supports multiple filters:
+ - name: Partial name matching (case-insensitive)
+ - email: Exact or partial email matching
+ - role: Filter by user role
+ - status: Filter by account status
+ - dateRange: Filter by creation date range
+ schema:
+ type: object
+ properties:
+ name:
+ type: string
+ description: Partial name search (minimum 2 characters)
+ email:
+ type: string
+ description: Email search pattern
+ role:
+ type: string
+ enum: [user, admin, moderator]
+ status:
+ type: string
+ enum: [active, inactive, suspended]
+```
+
+## Add error documentation
+
+Enhance error handling information:
+
+```yaml title="overlay.yml" {7-25}
+overlay: 1.0.0
+info:
+ title: Error Documentation
+ version: 1.0.0
+actions:
+ - target: $.methods[?(@.name == 'order.create')]
+ update:
+ errors:
+ - code: -32001
+ message: Insufficient inventory
+ data:
+ type: object
+ properties:
+ productId:
+ type: string
+ availableQuantity:
+ type: integer
+ requestedQuantity:
+ type: integer
+ - code: -32002
+ message: Payment authorization failed
+ data:
+ type: object
+ properties:
+ paymentMethod:
+ type: string
+ errorCode:
+ type: string
+ retryAllowed:
+ type: boolean
+```
+
+## Multiple overlays
+
+You can apply multiple overlay files in sequence:
+
+```yaml title="generators.yml" {4-8}
+api:
+ specs:
+ - spec: openrpc.yml
+ overlays:
+ - base-overlay.yml
+ - sdk-overlay.yml
+ - docs-overlay.yml
+ - examples-overlay.yml
+```
+
+## Environment-specific overlays
+
+Use different overlays for different environments:
+
+```yaml title="generators.yml" {4-7, 12-15}
+groups:
+ production:
+ audiences: [public]
+ specs:
+ - spec: openrpc.yml
+ overlays:
+ - production-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+ internal:
+ audiences: [admin, internal]
+ specs:
+ - spec: openrpc.yml
+ overlays:
+ - internal-overlay.yml
+ generators:
+ - name: fernapi/fern-typescript-node-sdk
+ version: 0.8.8
+```
+
+Overlays are applied in order, allowing you to build up customizations incrementally while keeping your original OpenRPC specification clean and focused on the core API definition.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/overview.mdx b/fern/products/openrpc-def/pages/overview.mdx
new file mode 100644
index 000000000..17341b14b
--- /dev/null
+++ b/fern/products/openrpc-def/pages/overview.mdx
@@ -0,0 +1,167 @@
+---
+title: What is an OpenRPC Specification?
+subtitle: OpenRPC is a standard for documenting JSON-RPC APIs
+---
+
+The OpenRPC Specification is a framework used by developers to document JSON-RPC APIs. The specification
+is written in JSON or YAML and contains all of your methods, parameters, schemas, and server configurations.
+Fern is compatible with OpenRPC specification [v1.3.2](https://spec.open-rpc.org/) and [v1.2.6](https://github.com/open-rpc/spec/releases/tag/1.2.6).
+
+ Considering options to generate an OpenRPC spec? Get live support [here](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)
+
+Below is an example of an OpenRPC file:
+
+```yaml openrpc.yml
+openrpc: 1.3.2
+info:
+ title: Calculator API
+ version: 1.0.0
+ description: |
+ A simple calculator API that performs basic arithmetic operations
+ using JSON-RPC 2.0 protocol.
+servers:
+ - name: production
+ url: https://api.calculator.com/rpc
+ description: Production JSON-RPC server
+ - name: development
+ url: http://localhost:8080/rpc
+ description: Development server
+methods:
+ - name: add
+ summary: Add two numbers
+ description: Performs addition of two numeric values
+ params:
+ - name: a
+ schema:
+ type: number
+ required: true
+ description: First number to add
+ - name: b
+ schema:
+ type: number
+ required: true
+ description: Second number to add
+ result:
+ name: sum
+ schema:
+ type: number
+ description: The sum of the two numbers
+ examples:
+ - name: AddExample
+ description: Example of adding two numbers
+ params:
+ a: 5
+ b: 3
+ result: 8
+ - name: divide
+ summary: Divide two numbers
+ description: Performs division of two numeric values
+ params:
+ - name: dividend
+ schema:
+ type: number
+ required: true
+ description: Number to be divided
+ - name: divisor
+ schema:
+ type: number
+ required: true
+ description: Number to divide by
+ result:
+ name: quotient
+ schema:
+ type: number
+ description: The result of the division
+ errors:
+ - code: -32602
+ message: Division by zero
+ data:
+ type: object
+ properties:
+ error:
+ type: string
+ const: "Cannot divide by zero"
+ examples:
+ - name: DivideExample
+ description: Example of dividing two numbers
+ params:
+ dividend: 10
+ divisor: 2
+ result: 5
+ - name: notify_calculation
+ summary: Notify about calculation
+ description: Send a notification about a completed calculation (no response expected)
+ params:
+ - name: operation
+ schema:
+ type: string
+ enum: [add, subtract, multiply, divide]
+ required: true
+ - name: result
+ schema:
+ type: number
+ required: true
+ - name: timestamp
+ schema:
+ type: string
+ format: date-time
+ required: true
+components:
+ schemas:
+ CalculationRequest:
+ type: object
+ properties:
+ operation:
+ type: string
+ enum: [add, subtract, multiply, divide]
+ operands:
+ type: array
+ items:
+ type: number
+ minItems: 2
+ maxItems: 2
+ precision:
+ type: integer
+ minimum: 0
+ maximum: 10
+ default: 2
+ required:
+ - operation
+ - operands
+ CalculationResult:
+ type: object
+ properties:
+ result:
+ type: number
+ operation:
+ type: string
+ timestamp:
+ type: string
+ format: date-time
+ required:
+ - result
+ - operation
+ - timestamp
+```
+
+## Setup your fern folder
+
+Start by initializing your fern folder with an OpenRPC spec
+
+
+```sh file
+fern init --openrpc ./path/to/openrpc
+```
+```sh url
+fern init --openrpc https://host/path/to/openrpc
+```
+
+
+This will initialize a directory like the following
+```
+fern/
+ ├─ fern.config.json
+ ├─ generators.yml
+ └─ openrpc/
+ ├─ openrpc.yml
+```
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/allof.mdx b/fern/products/openrpc-def/pages/schemas/allof.mdx
deleted file mode 100644
index 305b424b5..000000000
--- a/fern/products/openrpc-def/pages/schemas/allof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: All Of
----
-
-How to use the `allOf` keyword to compose schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/anyof.mdx b/fern/products/openrpc-def/pages/schemas/anyof.mdx
deleted file mode 100644
index bd293f9da..000000000
--- a/fern/products/openrpc-def/pages/schemas/anyof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Any Of
----
-
-How to use the `anyOf` keyword to allow multiple possible schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/enums.mdx b/fern/products/openrpc-def/pages/schemas/enums.mdx
deleted file mode 100644
index 269806428..000000000
--- a/fern/products/openrpc-def/pages/schemas/enums.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Enums
----
-
-How to use enum types to define a fixed set of values in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/inlined-schemas.mdx b/fern/products/openrpc-def/pages/schemas/inlined-schemas.mdx
deleted file mode 100644
index 50d864e10..000000000
--- a/fern/products/openrpc-def/pages/schemas/inlined-schemas.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Inlined Schemas
----
-
-How to define schemas inline within your OpenAPI specification.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/objects.mdx b/fern/products/openrpc-def/pages/schemas/objects.mdx
deleted file mode 100644
index 99c6a6e41..000000000
--- a/fern/products/openrpc-def/pages/schemas/objects.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Objects
----
-
-How to define object schemas with properties in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/oneof.mdx b/fern/products/openrpc-def/pages/schemas/oneof.mdx
deleted file mode 100644
index c29355abe..000000000
--- a/fern/products/openrpc-def/pages/schemas/oneof.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: One Of
----
-
-How to use the `oneOf` keyword to define alternative schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/schemas/override-type.mdx b/fern/products/openrpc-def/pages/schemas/override-type.mdx
deleted file mode 100644
index 44cac7698..000000000
--- a/fern/products/openrpc-def/pages/schemas/override-type.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Override Type
----
-
-How to override the default type inference for schemas in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/security-schemes/multiple-security-schemes.mdx b/fern/products/openrpc-def/pages/security-schemes/multiple-security-schemes.mdx
deleted file mode 100644
index 04a3407ed..000000000
--- a/fern/products/openrpc-def/pages/security-schemes/multiple-security-schemes.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Security Schemes
----
-
-Learn how to define and use multiple security schemes in your OpenAPI definition.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/security-schemes/overriding-security-scheme.mdx b/fern/products/openrpc-def/pages/security-schemes/overriding-security-scheme.mdx
deleted file mode 100644
index 905a45251..000000000
--- a/fern/products/openrpc-def/pages/security-schemes/overriding-security-scheme.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Security Scheme
----
-
-How to override security schemes in specific operations or contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/security-schemes/overview.mdx b/fern/products/openrpc-def/pages/security-schemes/overview.mdx
deleted file mode 100644
index 7743f8089..000000000
--- a/fern/products/openrpc-def/pages/security-schemes/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Security Schemes Overview
----
-
-Overview of security schemes and authentication methods in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/servers.mdx b/fern/products/openrpc-def/pages/servers.mdx
new file mode 100644
index 000000000..f00bf68c6
--- /dev/null
+++ b/fern/products/openrpc-def/pages/servers.mdx
@@ -0,0 +1,194 @@
+---
+title: Servers
+description: Configure server URLs and transports for your JSON-RPC API
+subtitle: Define server URLs and transport mechanisms for your JSON-RPC API
+---
+
+OpenRPC allows you to specify one or more server configurations that define how clients can connect to your JSON-RPC API.
+
+```yml openrpc.yml
+servers:
+ - name: production
+ url: https://api.yourcompany.com/rpc
+ description: Production HTTP JSON-RPC server
+ - name: websocket
+ url: wss://api.yourcompany.com/rpc
+ description: Production WebSocket JSON-RPC server
+```
+
+Specifying servers is valuable for both SDKs and Docs:
+- For SDKs, your users won't need to manually specify the server URL at client instantiation
+- For Docs, your API playground will automatically connect to the correct server
+
+## Transport protocols
+
+JSON-RPC can be used over various transport protocols:
+
+### HTTP/HTTPS Transport
+```yml openrpc.yml
+servers:
+ - name: http-production
+ url: https://api.yourcompany.com/rpc
+ description: HTTPS JSON-RPC endpoint
+ - name: http-staging
+ url: https://staging-api.yourcompany.com/rpc
+ description: Staging HTTPS endpoint
+```
+
+### WebSocket Transport
+```yml openrpc.yml
+servers:
+ - name: websocket-production
+ url: wss://api.yourcompany.com/rpc
+ description: WebSocket JSON-RPC for real-time communication
+ - name: websocket-dev
+ url: ws://localhost:8080/rpc
+ description: Development WebSocket server
+```
+
+### TCP Transport
+```yml openrpc.yml
+servers:
+ - name: tcp-production
+ url: tcp://api.yourcompany.com:9090
+ description: TCP JSON-RPC server for high-performance applications
+```
+
+## Server variables
+
+Use variables to make your server configurations more flexible:
+
+```yml openrpc.yml {3-12}
+servers:
+ - name: production
+ url: https://{environment}.api.yourcompany.com/rpc
+ description: Production JSON-RPC server
+ variables:
+ environment:
+ default: prod
+ enum:
+ - prod
+ - staging
+ - dev
+ description: Environment name
+```
+
+## Multiple environments
+
+Configure different environments with appropriate descriptions:
+
+```yml openrpc.yml
+servers:
+ - name: production
+ url: https://api.yourcompany.com/rpc
+ description: Production environment - high availability, rate limited
+ - name: staging
+ url: https://staging-api.yourcompany.com/rpc
+ description: Staging environment - for testing new features
+ - name: development
+ url: http://localhost:8080/rpc
+ description: Local development server
+ - name: sandbox
+ url: https://sandbox-api.yourcompany.com/rpc
+ description: Sandbox environment - safe for testing
+```
+
+## Server-specific configurations
+
+Add server-specific metadata and configurations:
+
+```yml openrpc.yml {4-12}
+servers:
+ - name: production
+ url: https://api.yourcompany.com/rpc
+ description: Production JSON-RPC server
+ variables:
+ version:
+ default: v1
+ enum: [v1, v2]
+ description: API version
+ summary: High-performance production server
+ tags:
+ - name: production
+ description: Production environment
+```
+
+## Load balancing and multiple URLs
+
+Configure multiple server URLs for load balancing:
+
+```yml openrpc.yml
+servers:
+ - name: primary
+ url: https://api-primary.yourcompany.com/rpc
+ description: Primary production server
+ - name: secondary
+ url: https://api-secondary.yourcompany.com/rpc
+ description: Secondary production server (fallback)
+ - name: regional-us
+ url: https://us.api.yourcompany.com/rpc
+ description: US regional server
+ - name: regional-eu
+ url: https://eu.api.yourcompany.com/rpc
+ description: EU regional server
+```
+
+## WebSocket-specific configurations
+
+Configure WebSocket servers with connection parameters:
+
+```yml openrpc.yml {4-10}
+servers:
+ - name: websocket-main
+ url: wss://api.yourcompany.com/rpc
+ description: Main WebSocket JSON-RPC server
+ variables:
+ protocol:
+ default: "jsonrpc-2.0"
+ description: JSON-RPC protocol version
+ heartbeat:
+ default: "30"
+ description: Heartbeat interval in seconds
+```
+
+## Security configurations
+
+Configure authentication and security at the server level:
+
+```yml openrpc.yml {4-6}
+servers:
+ - name: secure-production
+ url: https://api.yourcompany.com/rpc
+ description: Secure production server with authentication
+ security:
+ - bearerAuth: []
+ - apiKeyAuth: []
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ apiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+```
+
+## Transport-specific optimizations
+
+Configure servers for specific use cases:
+
+```yml openrpc.yml
+servers:
+ - name: bulk-operations
+ url: https://bulk.api.yourcompany.com/rpc
+ description: Optimized for batch requests and bulk operations
+ - name: real-time
+ url: wss://realtime.api.yourcompany.com/rpc
+ description: WebSocket server optimized for real-time notifications
+ - name: analytics
+ url: https://analytics.api.yourcompany.com/rpc
+ description: Dedicated server for analytics and reporting methods
+```
+
+Server configurations help clients understand how to connect to your JSON-RPC API and which transport mechanism is most appropriate for their use case.
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/servers/multiple-server-urls.mdx b/fern/products/openrpc-def/pages/servers/multiple-server-urls.mdx
deleted file mode 100644
index 94ba2c929..000000000
--- a/fern/products/openrpc-def/pages/servers/multiple-server-urls.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Multiple Server URLs
----
-
-How to configure multiple server URLs for different environments.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/servers/naming-your-servers.mdx b/fern/products/openrpc-def/pages/servers/naming-your-servers.mdx
deleted file mode 100644
index fd2a055bb..000000000
--- a/fern/products/openrpc-def/pages/servers/naming-your-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Naming Your Servers
----
-
-Best practices for naming your server configurations.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/servers/overriding-servers.mdx b/fern/products/openrpc-def/pages/servers/overriding-servers.mdx
deleted file mode 100644
index 0c5cc5f6f..000000000
--- a/fern/products/openrpc-def/pages/servers/overriding-servers.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Overriding Servers
----
-
-How to override server configurations in specific contexts.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file
diff --git a/fern/products/openrpc-def/pages/servers/overview.mdx b/fern/products/openrpc-def/pages/servers/overview.mdx
deleted file mode 100644
index 761a9d085..000000000
--- a/fern/products/openrpc-def/pages/servers/overview.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Servers Overview
----
-
-Overview of server configuration options in OpenAPI.
-
-This page is a WIP, please refer to our previous [documentation](https://buildwithfern.com/learn/api-definition/introduction/what-is-an-api-definition).
\ No newline at end of file