Publishing to NuGet

View as Markdown

Publish your public-facing Fern C#/.NET SDK to the NuGet registry. After following the steps on this page, you’ll have a versioned package published on NuGet.

Already publishing to NuGet?

If you’re using API key authentication, consider switching to OIDC (trusted publishing). See Migrating from API key to OIDC publishing for the steps.

Versioned package published on NuGet

This page assumes that you have:

  • An initialized fern folder, a GitHub repository for your C#/.NET SDK, and a C#/.NET generator group in generators.yml. See Generating an SDK (C#/.NET).

Configure SDK package settings

Update your generators.yml file to configure the package name, output location, and client name for NuGet publishing. Your generators.yml should live in your source repository (or on your local machine), not the repository that contains your C#/.NET SDK code.

1

Configure output location

In the group for your C#/.NET SDK, change the output location from local-file-system (the default) to nuget to indicate that Fern should publish your package directly to the NuGet registry:

generators.yml
1groups:
2 csharp-sdk:
3 generators:
4 - name: fern-csharp-sdk
5 version: 2.69.1
6 output:
7 location: nuget
2

Add a unique package name

Your package name must be unique in the NuGet registry, otherwise publishing your SDK will fail.

generators.yml
1groups:
2 csharp-sdk:
3 generators:
4 - name: fern-csharp-sdk
5 version: 2.69.1
6 output:
7 location: nuget
8 package-name: your-package-name
3

Configure client-class-name

The client-class-name option controls the name of the generated client class.

generators.yml
1groups:
2 csharp-sdk:
3 generators:
4 - name: fern-csharp-sdk
5 version: 2.69.1
6 output:
7 location: nuget
8 package-name: your-package-name
9 config:
10 client_class_name: YourClientName # must be PascalCase

Configure GitHub publishing

Fern can automatically publish your SDK to NuGet via GitHub Actions. Configure your GitHub repository and publishing mode:

Optionally set the mode to control how Fern handles SDK publishing:

  • mode: release (default): Fern generates code, commits to the default branch (or the branch you specify), and tags a release automatically
  • mode: pull-request (recommended): Fern generates code and creates a PR for you to review before release
  • mode: push: Fern generates code and pushes to a branch you specify for you to review before release

You can also configure other settings, like the reviewers or license. Refer to the full github (generators.yml) reference for more information.

generators.yml
1groups:
2 csharp-sdk:
3 generators:
4 - name: fern-csharp-sdk
5 version: 2.69.1
6 output:
7 location: nuget
8 package-name: your-package-name
9 config:
10 client_class_name: YourClientName
11 github:
12 repository: your-org/company-csharp
13 mode: push # or "pull-request"
14 branch: your-branch-name # Required for mode: push

Configure authentication

Choose how you want to authenticate with NuGet when publishing. OpenID Connect (OIDC) authentication is recommended because it removes the need to manage long-lived API keys.

1

Generate a NuGet API key

  1. Log into NuGet or create a new account

  2. Click on your profile picture

  3. Select API Keys, then Create

  4. Name your key

  5. Select Push > Push new packages and package versions as the Select Scopes type

  6. Enter * under Select Packages > Glob Pattern

    Replacing an existing NuGet package

    If you are overriding an existing package, select the relevant package instead of entering *.

  7. Click Create

Creating a new API key
Save your new key — it won’t be displayed after you leave the page.
2

Add API key to generators.yml

Add api-key: ${NUGET_API_KEY} to the output section:

generators.yml
1groups:
2 csharp-sdk:
3 generators:
4 - name: fern-csharp-sdk
5 version: 2.69.1
6 output:
7 location: nuget
8 package-name: your-package-name
9 api-key: ${NUGET_API_KEY}
10 config:
11 client_class_name: YourClientName
12 github:
13 repository: your-org/company-csharp
3

Add NUGET_API_KEY as a GitHub Actions secret

  1. Open your repository on GitHub and go to Settings
  2. Navigate to Secrets and variables > Actions
  3. Click New repository secret
  4. Name it NUGET_API_KEY and paste your NuGet API key
  5. Click Add secret
NUGET_API_KEY secret

Publish your SDK

Your SDK will automatically be published to NuGet when you create a GitHub release with a version tag:

  1. Create a GitHub release with a version tag (for example, v1.0.0)
  2. The CI workflow will run automatically and publish to NuGet
  3. View your package on nuget.org to confirm the version

If you prefer to trigger publishes manually from your source repository, set up a release workflow via GitHub Actions.

1

Add secret for your Fern API key

  1. Open your source repository on GitHub and go to Settings
  2. Navigate to Secrets and variables > Actions
  3. Select New repository secret
  4. Name your secret FERN_TOKEN
  5. Add your Fern API key. If you don’t already have one, generate one by running fern token. By default, the API key is generated for the organization listed in fern.config.json.
  6. Click Add secret

If you use API key authentication, also add a NUGET_API_KEY secret with your NuGet API key.

2

Set up a new workflow

Create a CI workflow that you can manually trigger from the GitHub UI. Navigate to Actions > New workflow > Set up workflow yourself:

.github/workflows/publish.yml
1name: Publish C#/.NET SDK
2
3on:
4 workflow_dispatch:
5 inputs:
6 version:
7 description: "The version of the C#/.NET SDK that you would like to release"
8 required: true
9 type: string
10
11jobs:
12 release:
13 runs-on: ubuntu-latest
14 steps:
15 - name: Checkout repo
16 uses: actions/checkout@v4
17
18 - name: Install Fern CLI
19 run: npm install -g fern-api
20
21 - name: Release C#/.NET SDK
22 env:
23 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
24 NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} # Only needed for API key authentication
25 run: |
26 fern generate --group csharp-sdk --version ${{ inputs.version }} --log-level debug
3

Regenerate and release your SDK

Navigate to the Actions tab, select the workflow, specify a version number, and click Run workflow. This regenerates your SDK.

Running C# publish workflow

The rest of the release process depends on your chosen mode:

  • Release mode (default): If you didn’t specify a mode or set mode: release, no further action is required. Fern automatically tags the new release with your specified version number and initiates the publishing workflow in your SDK repository.

  • Pull request or push mode: If you set mode: pull-request or mode: push, Fern creates a pull request or pushes to a branch respectively. Review and merge the PR (pull-request) or branch (push), then tag a new release to initiate the publishing workflow in your SDK repository.

Once the workflow completes, view your new release by logging into NuGet and navigating to Manage Packages.

1

Set NuGet environment variable

If you use API key authentication, set the NUGET_API_KEY environment variable on your local machine:

$export NUGET_API_KEY=your-actual-nuget-api-key
2

Regenerate and release your SDK

Regenerate your SDK, specifying the version:

$fern generate --group csharp-sdk --version <version>

The rest of the release process depends on your chosen mode:

  • Release mode (default): If you didn’t specify a mode or set mode: release, no further action is required. Fern automatically tags the new release with your specified version number and initiates the publishing workflow in your SDK repository.

  • Pull request or push mode: If you set mode: pull-request or mode: push, Fern creates a pull request or pushes to a branch respectively. Review and merge the PR (pull-request) or branch (push), then tag a new release to initiate the publishing workflow in your SDK repository.

Once the workflow completes, view your new release by logging into NuGet and navigating to Manage Packages.


Migrating from API key to OIDC publishing

If you’re using API key authentication and want to migrate to OIDC, follow these steps.

Why migrate to OIDC

OIDC-based publishing (trusted publishing) removes security risks associated with long-lived API keys:

  • Keys can be exposed in logs or configuration files
  • Compromised keys can be used persistently until manually revoked
  • Key rotation adds operational overhead

With OIDC, NuGet trusts your GitHub Actions workflow directly. The only secret required is NUGET_USER (your nuget.org profile name), which isn’t sensitive.

Prerequisites

Before migrating:

  • A package published to NuGet
  • A GitHub repository with GitHub Actions configured
  • Access to your account on nuget.org

Choose your migration path

Use this path if you can’t upgrade the generator or have customized your CI workflow.

When to use this path:

  • You can’t upgrade due to breaking changes or compatibility concerns
  • You’ve customized your CI workflow and added it to .fernignore
  • Path 1 didn’t update your workflow file
1

Configure trusted publishing on nuget.org

Follow the same instructions as Path 1 to create a trusted publishing policy on nuget.org.

2

Update your CI workflow manually

Open your .github/workflows/ci.yml file and make these changes to the publish job:

.github/workflows/ci.yml
1publish:
2 needs: [ compile, test ]
3 if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
4 runs-on: ubuntu-latest
5 permissions:
6 contents: read # ADD THIS: Required for actions/checkout@v4
7 id-token: write # ADD THIS: Required for OIDC
8 steps:
9 - name: Checkout repo
10 uses: actions/checkout@v4
11
12 - name: Setup .NET
13 uses: actions/setup-dotnet@v4
14
15 # ADD THIS: Exchange GitHub OIDC token for a short-lived NuGet API key
16 - name: NuGet OIDC login
17 uses: NuGet/login@v1
18 with:
19 nuget-server-url: https://api.nuget.org/v3/index.json
20 nuget-user: ${{ secrets.NUGET_USER }}
21
22 # MODIFY THIS: Remove env block with NUGET_API_KEY
23 - name: Publish to NuGet
24 run: dotnet nuget push src/*/bin/Release/*.nupkg --api-key ${{ env.NUGET_AUTH_TOKEN }} --source https://api.nuget.org/v3/index.json

Key changes from API key publishing:

  • Adds permissions with id-token: write and contents: read
  • Adds the NuGet/login@v1 step that exchanges the OIDC token for a short-lived API key
  • Removes any env blocks referencing NUGET_API_KEY
3

Add the NUGET_USER secret

The OIDC workflow requires a NUGET_USER secret. Follow the same instructions as Path 1, Step 4.

4

(Optional) Add ci.yml to .fernignore

Prevent future generator updates from overwriting your manual changes:

.fernignore
.github/workflows/ci.yml
5

Remove the NUGET_API_KEY secret

After verifying the migration works, remove the NUGET_API_KEY secret from your GitHub repository settings. A stale API key can interfere with OIDC authentication.

Verify your migration

After completing either migration path:

  1. Trigger a workflow run by creating a GitHub release with an alpha tag (e.g., v1.0.0-alpha)
  2. Check the workflow logs to verify the publish step succeeds
  3. Verify your package by visiting it on nuget.org to confirm the new version published correctly

Migration troubleshooting

Common causes:

  • Workflow filename doesn’t match exactly (must be ci.yml with the .yml extension)
  • Trusted publisher configuration on nuget.org doesn’t match your repository settings
  • Missing id-token: write or contents: read permissions in workflow
  • A stale NUGET_API_KEY secret is interfering with OIDC authentication
  • Using self-hosted runners (not supported)

Solution: Double-check your trusted publisher configuration on nuget.org matches your repository name and workflow filename exactly. Remove any old NUGET_API_KEY secret.

If your workflow continues using API key authentication:

  • Verify you’ve removed any env blocks referencing NUGET_API_KEY from the publish step
  • Ensure the publish job uses the NuGet/login@v1 action
  • Ensure you’re using a generator version that supports OIDC (if using Path 1)

For private repositories, the trust policy starts as temporarily active for 7 days. It becomes permanent after the first successful login binds the repository and owner IDs. Run the publish workflow within that window.