> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. # Publishing to NuGet > Learn how to publish your Fern-generated C#/.NET SDK to NuGet using OIDC or API key authentication, with step-by-step configuration and GitHub Actions setup. #### Enterprise feature This feature is available only for the [Enterprise plan](https://buildwithfern.com/pricing). To get started, reach out to [support@buildwithfern.com](mailto:support@buildwithfern.com). Publish your public-facing Fern C#/.NET SDK to the [NuGet registry](https://www.nuget.org/). After following the steps on this page, you'll have a versioned package published on NuGet. To distribute the SDK internally instead, generate to the local file system (optionally [self-hosted](/learn/sdks/deep-dives/self-hosted)) and build a `.nupkg` with [`fern generate --package`](/learn/cli-api-reference/cli-reference/sdk-commands#package). #### 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](#migrating-from-api-key-to-oidc-publishing) for the steps. ![Versioned package published on NuGet](/learn/_fern-img/c5a21d0bd777a975fd6a18cc8fcd151b81d2ea386ab89d47ab52a7d75047ce35.webp) 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)](/learn/sdks/generators/csharp/quickstart). Before proceeding, verify the prerequisites are actually in place: 1. **Source repo with `fern/` folder**: The user should be working in their source repo, not the SDK repo. Confirm `generators.yml` exists and has a C# generator group. 2. **C# generator group**: If they skipped the [C# quickstart](/learn/sdks/generators/csharp/quickstart), they need to run `fern add fern-csharp-sdk --group csharp-sdk` first. Default to OIDC authentication unless the user has a specific reason to use API key auth. OIDC is more secure and avoids managing NuGet API keys. ## 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](/learn/sdks/overview/project-structure) (or on your local machine), not the repository that contains your C#/.NET SDK code. #### 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`** ```yaml {6-7} title="generators.yml" groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget ``` #### Add a unique package name Your package name must be unique in the NuGet registry, otherwise publishing your SDK will fail. **`generators.yml`** ```yaml {8} title="generators.yml" groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name ``` #### Configure \`client-class-name\` The `client-class-name` option controls the name of the generated client class. **`generators.yml`** ```yaml {9-10} title="generators.yml" groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name config: client_class_name: YourClientName # must be PascalCase ``` The description, authors, tags, copyright, icon, and repository links on your package's nuget.org listing come from the [`package-metadata`](/learn/sdks/generators/csharp/configuration#package-metadata) config option. ## 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](/learn/sdks/reference/generators-yml#github) for more information. **`generators.yml`** ```yaml title="generators.yml" {11-14} groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name config: client_class_name: YourClientName github: repository: your-org/company-csharp mode: push # or "pull-request" 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. #### OIDC authentication (Recommended) OIDC-based publishing (also known as "trusted publishing") is the most secure way to publish. With OIDC, you don't need to manage API keys — NuGet trusts your GitHub repository to publish directly via short-lived tokens scoped to your workflow. #### Add OIDC to generators.yml Add `api-key: OIDC` to the `output` section: **`generators.yml`** ```yaml title="generators.yml" {9} groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name api-key: OIDC config: client_class_name: YourClientName github: repository: your-org/company-csharp ``` #### Generate your SDK Generate your SDK to create the GitHub Actions workflow with OIDC configuration: ```bash fern generate --group csharp-sdk ``` This creates a `.github/workflows/ci.yml` file configured to use OIDC for NuGet publishing. The generated workflow includes: * A `publish` job with `permissions: { contents: read, id-token: write }` * The [`NuGet/login@v1`](https://github.com/NuGet/login) action, which exchanges a GitHub OIDC token for a short-lived NuGet API key (\~1 hour) * `dotnet nuget push` using the temporary API key from the login step Alternatively, you can push your `generators.yml` changes and let the Fern GitHub Action generate the workflow for you. #### Authorize your repository on nuget.org Configure trusted publishing on nuget.org to allow your GitHub repository to publish: 1. Go to [nuget.org](https://www.nuget.org/) and click your user avatar 2. Select **Trusted Publishing** > **Create policy** 3. Fill in: * **Repository Owner**: Your GitHub organization or username * **Repository**: Your C# SDK repository name * **Workflow File**: `ci.yml` (filename only, without the `.github/workflows/` prefix) 4. Choose the policy owner (organization profile or personal account). The policy applies to all packages owned by that profile. #### Set the \`NUGET\_USER\` GitHub Actions secret The generated workflow requires a `NUGET_USER` secret containing your nuget.org profile name (not your email). 1. Open your SDK repository on GitHub and go to **Settings** 2. Navigate to **Secrets and variables** > **Actions** 3. Click **New repository secret** 4. Name it `NUGET_USER` and enter your nuget.org profile name 5. Click **Add secret** 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 7-day window. #### Troubleshooting **"Unable to authenticate" error** Common causes: * Workflow filename doesn't match exactly (must be `ci.yml`) * Trusted publisher configuration on nuget.org doesn't match your repository settings * A stale `NUGET_API_KEY` secret is interfering with OIDC authentication **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 from your repository. **Private repository limitations** For private repositories, the trust policy is temporarily active for 7 days. It becomes permanent after the first successful login. Run the publish workflow within that window. #### API key authentication (Legacy) #### Generate a NuGet API key 1. Log into [NuGet](https://nuget.org/) 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](/learn/_fern-img/7d0063cf1d598f3a137208bed660abcabcc990d8936e2d10cca376803c7214df.webp) Save your new key — it won't be displayed after you leave the page. #### Add API key to generators.yml Add `api-key: ${NUGET_API_KEY}` to the `output` section: **`generators.yml`** ```yaml title="generators.yml" {9} groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name api-key: ${NUGET_API_KEY} config: client_class_name: YourClientName github: repository: your-org/company-csharp ``` #### 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](/learn/_fern-img/fe114ac818cc31b427a032317ea8eeeff188210ced36200aaf3b970284b783a9.webp) ## 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 #### Alternative: Manual workflow dispatch If you prefer to trigger publishes manually from your source repository, set up a release workflow via [GitHub Actions](https://docs.github.com/en/actions). #### 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. #### 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`** ```yaml title=".github/workflows/publish.yml" maxLines=0 name: Publish C#/.NET SDK on: workflow_dispatch: inputs: version: description: "The version of the C#/.NET SDK that you would like to release" required: true type: string jobs: release: runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 - name: Install Fern CLI run: npm install -g fern-api - name: Release C#/.NET SDK env: FERN_TOKEN: ${{ secrets.FERN_TOKEN }} NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} # Only needed for API key authentication run: | fern generate --group csharp-sdk --version ${{ inputs.version }} --log-level debug ``` #### 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](/learn/_fern-img/519254f94e2afce5b51212349518eb55548d713cc84d77f5aebd1bcc2d688495.webp) 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](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) 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**. #### Alternative: Release via CLI and environment variables #### Set NuGet environment variable If you use API key authentication, set the `NUGET_API_KEY` environment variable on your local machine: ```bash export NUGET_API_KEY=your-actual-nuget-api-key ``` #### Regenerate and release your SDK Regenerate your SDK, specifying the version: ```bash fern generate --group csharp-sdk --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](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) 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](https://www.nuget.org/) * A GitHub repository with GitHub Actions configured * Access to your account on [nuget.org](https://www.nuget.org/) ### Choose your migration path #### Path 1: Upgrade your generator (Recommended) This is the easiest path if you can upgrade to a version of the C# SDK generator that supports OIDC. **When to use this path:** * You can upgrade to a Fern C# SDK generator version that supports OIDC * You haven't `.fernignore`'d your CI workflow file #### Configure trusted publishing on nuget.org 1. Go to [nuget.org](https://www.nuget.org/) and click your user avatar 2. Select **Trusted Publishing** > **Create policy** 3. Fill in: * **Repository Owner**: Your GitHub organization or username * **Repository**: Your C# SDK repository name * **Workflow File**: `ci.yml` (filename only, without the `.github/workflows/` prefix) 4. Choose the policy owner (organization profile or personal account). The policy applies to all packages owned by that profile. #### Update your generators.yml Change the `output.api-key` field from `${NUGET_API_KEY}` to `OIDC`: **`generators.yml`** ```yaml title="generators.yml" groups: csharp-sdk: generators: - name: fern-csharp-sdk version: 2.83.3 output: location: nuget package-name: your-package-name api-key: OIDC # Changed from ${NUGET_API_KEY} config: client_class_name: YourClientName github: repository: your-org/company-csharp ``` #### Regenerate your SDK Regenerate the SDK to update the CI workflow with OIDC configuration: **Locally:** ```bash fern generate --group csharp-sdk ``` **Or via GitHub Actions:** Push your updated `generators.yml` and let the Fern GitHub Action regenerate the SDK. This updates `.github/workflows/ci.yml` with the required OIDC permissions and switches to the `NuGet/login@v1` action. #### Add the NUGET\_USER secret The OIDC workflow requires a `NUGET_USER` secret containing your nuget.org profile name (not your email). 1. Open your SDK repository on GitHub and go to **Settings** 2. Navigate to **Secrets and variables** > **Actions** 3. Click **New repository secret** 4. Name it `NUGET_USER` and enter your nuget.org profile name 5. Click **Add secret** #### Remove the NUGET\_API\_KEY secret After verifying the migration works, remove the `NUGET_API_KEY` secret from your GitHub repository settings (**Settings** > **Secrets and variables** > **Actions**). A stale API key can interfere with OIDC authentication. #### Path 2: Manual CI workflow update 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 #### Configure trusted publishing on nuget.org Follow the same instructions as Path 1 to create a trusted publishing policy on nuget.org. #### Update your CI workflow manually Open your `.github/workflows/ci.yml` file and make these changes to the `publish` job: **`.github/workflows/ci.yml`** ```yaml title=".github/workflows/ci.yml" publish: needs: [ compile, test ] if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest permissions: contents: read # ADD THIS: Required for actions/checkout@v4 id-token: write # ADD THIS: Required for OIDC steps: - name: Checkout repo uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 # ADD THIS: Exchange GitHub OIDC token for a short-lived NuGet API key - name: NuGet OIDC login uses: NuGet/login@v1 with: nuget-server-url: https://api.nuget.org/v3/index.json nuget-user: ${{ secrets.NUGET_USER }} # MODIFY THIS: Remove env block with NUGET_API_KEY - name: Publish to NuGet 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` #### Add the NUGET\_USER secret The OIDC workflow requires a `NUGET_USER` secret. Follow the same instructions as Path 1, Step 4. #### (Optional) Add ci.yml to .fernignore Prevent future generator updates from overwriting your manual changes: **`.fernignore`** ```text title=".fernignore" .github/workflows/ci.yml ``` #### 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](https://www.nuget.org/) to confirm the new version published correctly ### Migration troubleshooting #### "Unable to authenticate" error **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. #### Workflow still using NUGET\_API\_KEY 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) #### Private repository limitations 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. > Learn how to publish your Fern-generated C#/.NET SDK to NuGet using OIDC or API key authentication, with step-by-step configuration and GitHub Actions setup.