diff --git a/.vale/styles/FernStyles/Headings.yml b/.vale/styles/FernStyles/Headings.yml
index 856750e8d..50eced3e8 100644
--- a/.vale/styles/FernStyles/Headings.yml
+++ b/.vale/styles/FernStyles/Headings.yml
@@ -90,3 +90,4 @@ exceptions:
- OG
- BCP
- ISO
+ - Replay
diff --git a/fern/products/cli-api-reference/pages/commands.mdx b/fern/products/cli-api-reference/pages/commands.mdx
index 0a5295d19..821e0ada0 100644
--- a/fern/products/cli-api-reference/pages/commands.mdx
+++ b/fern/products/cli-api-reference/pages/commands.mdx
@@ -38,6 +38,8 @@ hideOnThisPage: true
| [`fern generate`](#fern-generate) | Build & publish SDK updates |
| [`fern write-overrides`](#fern-write-overrides) | Create OpenAPI customizations |
| [`fern generator upgrade`](#fern-generator-upgrade) | Update SDK generators to latest versions |
+| [`fern replay resolve`](#fern-replay-resolve) | Resolve patch conflicts left by [Fern Replay](/learn/sdks/overview/custom-code#replay) |
+| [`fern replay forget`](#fern-replay-forget) | Remove tracked customization patches from `.fern/replay.lock` |
## Detailed command documentation
@@ -173,7 +175,7 @@ hideOnThisPage: true
```bash
- fern generate [--group ] [--api ] [--version ] [--preview] [--fernignore ] [--local] [--force]
+ fern generate [--group ] [--api ] [--version ] [--preview] [--fernignore ] [--local] [--force] [--no-replay]
```
@@ -270,6 +272,18 @@ hideOnThisPage: true
fern generate --group python-sdk --force
```
+ ### no-replay
+
+ Use `--no-replay` to skip [Fern Replay](/learn/sdks/overview/custom-code#replay)'s patch detection and application phase for a single generation. The generation still produces the new SDK code, but tracked customization patches aren't applied on top. Useful for a clean regeneration — for example, to debug whether a conflict is caused by a specific patch.
+
+ ```bash
+ fern generate --no-replay --local
+ ```
+
+
+ `--no-replay` works only for local generation (`--local`).
+
+
@@ -708,6 +722,75 @@ hideOnThisPage: true
Use `--include-major` to include major version upgrades. Major versions are skipped by default to prevent breaking changes.
+
+
+
+ Use `fern replay resolve` to work through patch conflicts that [Fern Replay](/learn/sdks/overview/custom-code#replay) couldn't merge cleanly during `fern generate`. The command runs in two phases: the first run applies unresolved patches to your working tree with standard merge markers (`<<<<<<<` / `=======` / `>>>>>>>`) for you to edit; the second run verifies no markers remain and commits the resolved patches.
+
+
+ ```bash
+ fern replay resolve [directory] [--no-check-markers]
+ ```
+
+
+ ### directory
+
+ Path to the SDK directory containing `.fern/replay.lock`. Defaults to the current directory.
+
+ ### no-check-markers
+
+ Skip the conflict-marker check before committing. Use with caution — committing files that still contain merge markers will break the next regeneration.
+
+
+
+
+
+ Use `fern replay forget` to remove tracked customization patches from `.fern/replay.lock`. Useful when the generator now supports a customization natively, or when you want to stop replaying a specific edit on future regenerations.
+
+
+ ```bash
+ fern replay forget ... [--all] [--dry-run] [--yes]
+ ```
+
+
+ The command accepts three modes:
+
+ - **By patch ID.** Pass one or more patch IDs to remove specific patches.
+
+ ```bash
+ fern replay forget patch-abc123 patch-def456
+ ```
+
+ - **By search pattern.** Pass a pattern to find matching patches. The command shows the matches with diff stats and prompts for confirmation before removing.
+
+ ```bash
+ fern replay forget "src/utils"
+ ```
+
+ - **All patches.** Pass `--all` to remove every tracked patch.
+
+ ```bash
+ fern replay forget --all
+ ```
+
+ ### dry-run
+
+ Show what would be removed without actually removing.
+
+ ```bash
+ fern replay forget "src/utils" --dry-run
+ ```
+
+ ### yes
+
+ Skip confirmation prompts. Required in non-interactive or CI environments.
+
+ ```bash
+ fern replay forget "src/utils" --yes
+ ```
+
+
+
Pulls the latest OpenAPI spec from the specified `origin` in `generators.yml` and
updates the local spec. Alternatively, you can [automate this process by setting up a GitHub Action](/api-definitions/openapi/sync-your-open-api-specification).
diff --git a/fern/products/sdks/custom-code.mdx b/fern/products/sdks/custom-code.mdx
index 4655c1866..dfdcdfa97 100644
--- a/fern/products/sdks/custom-code.mdx
+++ b/fern/products/sdks/custom-code.mdx
@@ -1,22 +1,25 @@
---
title: Adding custom code
headline: Adding custom code (overview)
-description: Extend Fern-generated SDKs with custom methods, logic, and dependencies. Use .fernignore to protect your code from being overwritten during regeneration.
+description: Extend Fern-generated SDKs with custom methods, logic, and dependencies. Use Fern Replay for line-level edits and .fernignore for files you own end-to-end.
---
Fern-generated SDKs are designed to be extended with custom logic, methods, and dependencies. If you want your SDK to do more than just make basic API calls (like combining multiple calls, processing data, adding utilities), you can add custom code that lives in harmony with the generated code.
-You can also add custom methods by inheriting the Fern generated client and extending it, plus add any dependencies that your custom methods depend on in your `generators.yml` file.
+Fern provides two complementary tools for keeping your customizations safe across regenerations:
-## Using `.fernignore` to preserve your customizations
+- **`.fernignore`** — Full-file ownership. The generator stops touching listed files entirely. Use for fully hand-written modules, READMEs, or custom workflows. Trade-off: you also stop receiving generator updates to those files.
+- **Replay** — Line-level edits. Keep generated files under generator control, and reapply your edits via 3-way merge on every regeneration.
-Once you add files containing custom code, use `.fernignore` to protect your custom code from being overwritten when Fern regenerates your SDK.
-
-Simply add your custom files to the SDK repository and list them in `.fernignore`. Fern won't override any files listed there. A `.fernignore` file is automatically created in your SDK repository when you use GitHub publishing.
+## Full-file ownership with `.fernignore` [#fernignore]
`.fernignore` applies only to SDK generation. It has no effect on [Fern Docs](/learn/docs/getting-started/overview) builds.
+Use `.fernignore` to protect files you own end-to-end from being overwritten during SDK regeneration.
+
+Add your custom files to the SDK repository and list them in `.fernignore`. A `.fernignore` file is automatically created in your SDK repository when you use GitHub publishing.
+
Your `.fernignore` file might look something like this:
```gitignore title=".fernignore"
@@ -31,7 +34,8 @@ LICENSE
# Custom code
src/CustomClient.ts
```
-For another example, see Cohere's [`.fernignore` file for their TypeScript SDK](https://github.com/cohere-ai/cohere-typescript/blob/ad583e3003bd51e80a82317f9e16beec85881b86/.fernignore).
+
+For another real-world example, see Cohere's [`.fernignore` file for their TypeScript SDK](https://github.com/cohere-ai/cohere-typescript/blob/ad583e3003bd51e80a82317f9e16beec85881b86/.fernignore).
You'll have a separate `.fernignore` file for each of your SDKs:
@@ -61,24 +65,39 @@ You'll have a separate `.fernignore` file for each of your SDKs:
+## Line-level edits with Replay [#replay]
+
+
+Replay is on by default for SDKs using [`pull-request` output mode](/learn/sdks/reference/generators-yml#pull-request). To enable Replay on an SDK that's currently on `release` or `push` mode, switch to `pull-request` mode via the [migration guide](/learn/sdks/overview/replay-migration).
+
+Replay requires the latest Fern CLI and SDK generator versions — run `fern upgrade` to make sure you're current.
+
+
+Replay automatically preserves the edits you make to your generated SDK across regenerations. When you commit a change to generated code, Replay scans your repo's history since the last `[fern-generated]` commit and stores each customer commit as a tracked patch in `.fern/replay.lock`. On the next `fern generate`, those patches are reapplied to the freshly generated SDK via 3-way merge, and the result lands as a `[fern-replay]` commit on top of `[fern-generated]` in the regeneration PR:
+
+```text title="Regeneration PR"
+* abc123 (HEAD -> main) [fern-replay] Apply customizations
+* 789abc [fern-generated] Update SDK to spec rev 0451
+* 234bcd Add helpers on top of User type
+* ...
+```
+
+If the generator and your customization changed the same lines, Replay reports the conflict in the PR body. Run [`fern replay resolve`](/learn/cli-api-reference/cli-reference/commands#fern-replay-resolve) locally to walk through it.
+
+### Disable Replay
+
+To disable Replay for a specific generator, set `replay.enabled: false` in `generators.yml`. See the [`replay` reference](/learn/sdks/reference/generators-yml#replay) for global and per-generator configuration.
+
+## Customization patterns by language
+
+Each SDK generator has its own conventions for adding custom code: how to extend the generated client, where helpers go, how to declare dependencies. See the guide for your language:
- ## Augmenting your SDK with custom code
-
- Get started adding custom code to a specific SDK:
-
-
- } href="/sdks/generators/typescript/custom-code">
-
- } href="/sdks/generators/python/custom-code">
-
- } href="/learn/sdks/generators/go/custom-code">
-
- } href="/learn/sdks/generators/java/custom-code">
-
- } href="/sdks/generators/csharp/custom-code">
-
- } href="/sdks/generators/php/custom-code">
-
- } href="/sdks/generators/ruby/custom-code">
-
-
+
+ } href="/sdks/generators/typescript/custom-code" />
+ } href="/sdks/generators/python/custom-code" />
+ } href="/learn/sdks/generators/go/custom-code" />
+ } href="/learn/sdks/generators/java/custom-code" />
+ } href="/sdks/generators/csharp/custom-code" />
+ } href="/sdks/generators/php/custom-code" />
+ } href="/sdks/generators/ruby/custom-code" />
+
diff --git a/fern/products/sdks/generators/csharp/custom-code.mdx b/fern/products/sdks/generators/csharp/custom-code.mdx
index a1ab27ffa..14da9cb94 100644
--- a/fern/products/sdks/generators/csharp/custom-code.mdx
+++ b/fern/products/sdks/generators/csharp/custom-code.mdx
@@ -7,12 +7,12 @@ description: Add custom logic and methods to your .NET SDK with Fern. Extend Bas
This page covers how to add custom logic and methods to your .NET SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
```csharp title="src/YourNamespace/Helper.cs"
diff --git a/fern/products/sdks/generators/go/custom-code.mdx b/fern/products/sdks/generators/go/custom-code.mdx
index 59795942e..2480611d8 100644
--- a/fern/products/sdks/generators/go/custom-code.mdx
+++ b/fern/products/sdks/generators/go/custom-code.mdx
@@ -7,12 +7,12 @@ description: Learn how to add custom logic and methods to your Go SDK with Fern.
This page covers how to add custom logic and methods to your Go SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
### Create a new file and add your custom logic
diff --git a/fern/products/sdks/generators/java/custom-code.mdx b/fern/products/sdks/generators/java/custom-code.mdx
index 1a88c9577..01ded5d18 100644
--- a/fern/products/sdks/generators/java/custom-code.mdx
+++ b/fern/products/sdks/generators/java/custom-code.mdx
@@ -7,12 +7,12 @@ description: Augment your Java SDK with custom utilities
This page covers how to add custom logic, methods, and dependencies to your TypeScript SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
### Create a new file and add your custom logic
diff --git a/fern/products/sdks/generators/php/custom-code.mdx b/fern/products/sdks/generators/php/custom-code.mdx
index 848f1fa8d..b69f9873e 100644
--- a/fern/products/sdks/generators/php/custom-code.mdx
+++ b/fern/products/sdks/generators/php/custom-code.mdx
@@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your PH
This page covers how to add custom logic, methods, and dependencies to your PHP SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
```php title="src/Helper.php"
diff --git a/fern/products/sdks/generators/python/custom-code.mdx b/fern/products/sdks/generators/python/custom-code.mdx
index 3b8fcd6e0..b1c8e8cc5 100644
--- a/fern/products/sdks/generators/python/custom-code.mdx
+++ b/fern/products/sdks/generators/python/custom-code.mdx
@@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your Py
This page covers how to add custom logic, methods, and dependencies to your Python SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
### Create a new file and add your custom logic
diff --git a/fern/products/sdks/generators/ruby/custom-code.mdx b/fern/products/sdks/generators/ruby/custom-code.mdx
index fdb588a8b..64126ed79 100644
--- a/fern/products/sdks/generators/ruby/custom-code.mdx
+++ b/fern/products/sdks/generators/ruby/custom-code.mdx
@@ -7,12 +7,12 @@ description: Augment your Ruby SDK with custom utilities
This page covers how to add custom logic, methods, and dependencies to your Ruby SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
```ruby title="lib//helper.rb"
diff --git a/fern/products/sdks/generators/typescript/custom-code.mdx b/fern/products/sdks/generators/typescript/custom-code.mdx
index 8a8b090e8..b4fadb488 100644
--- a/fern/products/sdks/generators/typescript/custom-code.mdx
+++ b/fern/products/sdks/generators/typescript/custom-code.mdx
@@ -7,12 +7,12 @@ description: Learn how to add custom logic, methods, and dependencies to your Ty
This page covers how to add custom logic, methods, and dependencies to your TypeScript SDK.
-
-
## Adding custom logic
To get started adding custom code:
+
+
### Create a new file and add your custom logic
diff --git a/fern/products/sdks/reference/generators-yml-reference.mdx b/fern/products/sdks/reference/generators-yml-reference.mdx
index f18f7050f..a2d09f116 100644
--- a/fern/products/sdks/reference/generators-yml-reference.mdx
+++ b/fern/products/sdks/reference/generators-yml-reference.mdx
@@ -406,6 +406,19 @@ autorelease: true
Set to `true` to enable Autorelease, `false` to disable it. [Per-generator settings](#autorelease-2) override this global configuration.
+## `replay`
+
+Override [Fern Replay](/learn/sdks/overview/custom-code#replay) behavior globally. Replay runs based on your organization's configuration; use this setting to disable it across all generators in this `generators.yml`. Alternatively, you can [configure Replay for individual SDKs](#replay-1).
+
+```yaml title="generators.yml"
+replay:
+ enabled: false
+```
+
+
+ Set to `false` to skip Replay for all SDK generations, even if Replay is enabled for your organization. [Per-generator settings](#replay-1) override this global configuration.
+
+
## `aliases`
Define shortcuts that map to multiple generator groups, allowing you to run several groups with a single command. When you run `fern generate --group `, all groups in the alias run in parallel. You can also set an alias as your `default-group`.
@@ -1019,6 +1032,24 @@ groups:
Set to `true` to enable Autorelease for this generator, `false` to disable it. Per-generator settings override the global [`autorelease`](#autorelease-1) configuration.
+#### `replay`
+
+Override [Fern Replay](/learn/sdks/overview/custom-code#replay) for an individual SDK. Alternatively, you can [configure Replay globally for all SDKs](#replay).
+
+```yml title="generators.yml" {6-7}
+groups:
+ ts-sdk:
+ generators:
+ - name: fernapi/fern-typescript-sdk
+ ...
+ replay:
+ enabled: false
+```
+
+
+ Set to `false` to skip Replay for this generator, even if Replay is enabled for your organization or globally in this `generators.yml`. Per-generator settings override the global [`replay`](#replay) configuration.
+
+
#### `snippets`
Configures snippets for a particular generator.
diff --git a/fern/products/sdks/replay-migration.mdx b/fern/products/sdks/replay-migration.mdx
new file mode 100644
index 000000000..6687b0280
--- /dev/null
+++ b/fern/products/sdks/replay-migration.mdx
@@ -0,0 +1,238 @@
+---
+title: Migrating to Replay
+headline: Migrating from release or push mode to pull-request mode
+description: Step-by-step guide for switching SDK generation from release or push mode to pull-request mode so Fern Replay can run.
+---
+
+
+ Replay requires the latest Fern CLI and SDK generator versions. Run `fern upgrade` to make sure you're current.
+
+
+This guide migrates an SDK from `release` or `push` output mode to `pull-request` output mode — the mode required to enable [Fern Replay](/learn/sdks/overview/custom-code#replay). Only follow it if your SDK is currently on `release` or `push` mode and you want to switch in order to use Replay. SDKs already using `pull-request` mode have Replay on by default; no migration is needed.
+
+## What changes
+
+- SDK updates arrive as PRs instead of direct pushes to `main`.
+- Customizations to generated code survive regeneration via 3-way merge.
+- Version bumps are computed from pure generator output, never contaminated by customizations.
+- Publishing is decoupled from generation, so you control when to release.
+
+## Before you start
+
+- Install the [Fern GitHub App](https://github.com/apps/fern-api) on your SDK repositories.
+- Update to the latest Fern CLI and SDK generator versions (`fern upgrade`).
+
+## Phase 1: Move publishing secrets to the SDK repo
+
+Fern sets GitHub Actions secrets in your SDK repo on every generation. When you switch to PR mode, that write would clobber any secrets you set directly. To avoid this, move secret ownership from the config repo to the SDK repo before flipping modes.
+
+For each SDK repo:
+
+
+
+
+Find which secrets your `generators.yml` references for publishing:
+
+- Python: `PYPI_USERNAME`, `PYPI_PASSWORD`
+- TypeScript / npm: `NPM_TOKEN`
+- Java / Maven: `MAVEN_USERNAME`, `MAVEN_PASSWORD`, `MAVEN_SIGNATURE_KID`, `MAVEN_SIGNATURE_PASSWORD`, `MAVEN_SIGNATURE_SECRET_KEY`
+- Ruby: `RUBY_GEMS_API_KEY`
+- C# / NuGet: `NUGET_API_KEY`
+- Rust / Crates: `CRATES_TOKEN`
+
+
+
+
+
+Add the real publish credentials directly to the SDK repo: **Settings → Secrets and variables → Actions → New repository secret**.
+
+
+
+
+
+Remove the publish credential values from the config repo's `generators.yml`:
+
+```yaml title="generators.yml"
+# BEFORE
+groups:
+ python-sdk:
+ generators:
+ - name: fernapi/fern-python-sdk
+ version: "..."
+ github:
+ repository: customer/python-sdk
+ mode: pull-request
+ output:
+ pypi:
+ username: $PYPI_USERNAME
+ password: $PYPI_PASSWORD
+
+# AFTER (remove the output/pypi block entirely)
+groups:
+ python-sdk:
+ generators:
+ - name: fernapi/fern-python-sdk
+ version: "..."
+ github:
+ repository: customer/python-sdk
+ mode: pull-request
+```
+
+
+
+
+
+Run a generation. Confirm the SDK repo's secrets weren't overwritten by checking the "Last updated" timestamp under **SDK repo → Settings → Secrets**.
+
+
+
+
+## Phase 2: Switch output mode
+
+In `generators.yml`:
+
+```yaml title="generators.yml" {3}
+github:
+ repository: customer/python-sdk
+ mode: pull-request # was: mode: release (or: mode: push)
+```
+
+If you use `version: AUTO`, no other changes are needed. Autoversioning runs as part of the generator-cli pipeline and diffs pure generator output, so customer customizations never contaminate the version diff.
+
+## Phase 3: Decouple publishing from generation (optional)
+
+To control publishing on your own schedule rather than on every Fern generation, keep your publish workflow out of Fern's generation cycle:
+
+
+
+
+```text title=".fernignore"
+.github/workflows/ci.yml
+```
+
+
+
+
+
+Switch from push-to-main to manual or release-tagged:
+
+```yaml title=".github/workflows/ci.yml"
+# Instead of:
+on:
+ push:
+ branches: [main]
+
+# Use:
+on:
+ workflow_dispatch:
+ release:
+ types: [published]
+```
+
+
+
+
+
+Update any secret references if you renamed credentials in Phase 1.
+
+
+
+
+## Phase 4: First generation with replay
+
+
+
+
+Run `fern generate --group ` from the config repo (or trigger via your existing CI).
+
+
+
+
+
+The first run auto-creates `.fern/replay.lock`. Replay tracks customer commits to generated files from this point on.
+
+
+
+
+
+The PR contains:
+
+- `[fern-generated]`: pure generator output
+- `[fern-replay]`: patches re-applied (empty on first run, since no patches exist yet)
+- Updated `.fern/replay.lock`
+
+
+
+
+
+The next generation re-anchors correctly even after the squash.
+
+
+
+
+## Phase 5: Validate
+
+After the first generation and merge, confirm the following:
+
+- A PR was created (not a direct push to `main`).
+- No unintended package release was triggered.
+- SDK code is correct and passes CI.
+- `.fern/replay.lock` exists in the SDK repo.
+- `.fernignore` contains `replay.lock` (and `replay.yml`).
+
+After your first real customization, verify Replay's behavior end-to-end:
+
+- You edit a generated file, commit, and merge to `main`.
+- The next `fern generate` detects the patch (PR body shows `patches detected: 1`).
+- The customization survives regeneration via a clean 3-way merge.
+- `.fern/replay.lock` shows the patch stored.
+
+## Rollback
+
+Replay never modifies files destructively. Your `main` branch always has correct code, so rolling back is straightforward.
+
+**Quick disable.** If you only want to stop Replay and stay in `pull-request` mode, delete `.fern/replay.lock` from the SDK repo and commit. The next `fern generate` runs without Replay; PRs land in the same shape as before, just without the `[fern-replay]` commit.
+
+**Full revert to `release` or `push` mode:**
+
+
+
+
+```yaml title="generators.yml" {3}
+github:
+ repository: customer/python-sdk
+ mode: release # or mode: push
+```
+
+
+
+
+
+Re-add the credentials you removed in Phase 1.
+
+
+
+
+
+- Delete `.fern/replay.lock`
+- Delete `.fern/replay.yml`
+- Delete the `fern-generation-base` tag if it exists
+
+
+
+
+
+
+
+
+## Troubleshooting
+
+| Command | Effect |
+|---------|--------|
+| [`fern generate --no-replay`](/learn/cli-api-reference/cli-reference/commands#no-replay) | Skip patch application for one generation. Generation still creates `[fern-generated]`; patches aren't applied. Useful for debugging. |
+| [`fern replay forget`](/learn/cli-api-reference/cli-reference/commands#fern-replay-forget) | Untrack patches by ID, by pattern, or all at once. The next generation overwrites those files. |
+
+## Known caveats
+
+- **Closed-without-merge replay PRs.** If a replay PR is closed without merging, the next generation detects the abandoned state and re-anchors automatically. No manual cleanup needed.
+- **Force pushes and rewritten history.** Replay handles force-pushed branches via a fallback detection path. Existing patches still apply correctly.
diff --git a/fern/products/sdks/sdks.yml b/fern/products/sdks/sdks.yml
index 2e9f95fc9..18aa5f54d 100644
--- a/fern/products/sdks/sdks.yml
+++ b/fern/products/sdks/sdks.yml
@@ -20,6 +20,10 @@ navigation:
- page: Adding custom code
path: ./custom-code.mdx
slug: custom-code
+ - page: Migrating to Replay
+ hidden: true
+ path: ./replay-migration.mdx
+ slug: replay-migration
- page: Capabilities
path: ./capabilities.mdx
slug: capabilities
@@ -66,7 +70,7 @@ navigation:
path: ./generators/python/configuration.mdx
slug: configuration
- page: Adding custom code
- path: ./generators/python/custom-code.mdx
+ path: ./generators/python/custom-code.mdx
slug: custom-code
- page: Dynamic authentication
path: ./generators/python/dynamic-authentication.mdx
@@ -90,7 +94,7 @@ navigation:
path: ./generators/go/configuration.mdx
slug: configuration
- page: Adding custom code
- path: ./generators/go/custom-code.mdx
+ path: ./generators/go/custom-code.mdx
slug: custom-code
- changelog: ./generators/go/changelog
slug: changelog
@@ -264,7 +268,7 @@ navigation:
path: ./deep-dives/self-hosted.mdx
slug: self-hosted
- section: Reference
- contents:
+ contents:
- page: generators.yml
path: ./reference/generators-yml-reference.mdx
slug: generators-yml
diff --git a/fern/products/sdks/snippets/custom-code-callout.mdx b/fern/products/sdks/snippets/custom-code-callout.mdx
index 35db71efe..7cf2ce34a 100644
--- a/fern/products/sdks/snippets/custom-code-callout.mdx
+++ b/fern/products/sdks/snippets/custom-code-callout.mdx
@@ -1,2 +1,2 @@
-Before getting started, [read about how Fern SDKs use custom code and learn about the `.fernignore` file](/sdks/overview/custom-code).
\ No newline at end of file
+These steps cover adding a new custom file to the SDK. To preserve line-level edits to a generated file, use [Replay](/sdks/overview/custom-code#replay) instead.
\ No newline at end of file