> This page is for SDKs, version Legacy.
> For other versions, use one of these documentation indexes:
> - Current (default): https://buildwithfern.com/learn/sdks/current/llms.txt
> - Legacy: https://buildwithfern.com/learn/sdks/legacy/llms.txt

> 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 npm

> Learn how to publish your Fern TypeScript SDK to npm using OIDC or token-based authentication. Complete guide with GitHub Actions setup.

> **You're viewing the legacy SDK docs**
>
> These pages cover SDK generation configured with `generators.yml`. New projects use `sdk-config.yml`, so see [the Current version of this page](/learn/sdks/generators/typescript/publishing) for up-to-date guidance. To move an existing project, follow the [SDK Config migration guide](/learn/sdks/legacy/generators/migration-guides/sdk-config), which walks through `fern sdk migrate`, and review the [`sdk-config.yml` reference](/learn/sdks/reference/sdk-config).

Publish your public-facing Fern TypeScript SDK to the [npmjs registry](https://www.npmjs.com/). After following the steps on this page,
you'll have a versioned package published on npm. To distribute the SDK
internally instead, generate to the local file system (optionally
[self-hosted](/learn/sdks/legacy/deep-dives/self-hosted)) and build a tarball with
[`fern generate --package`](/learn/cli-api-reference/cli-reference/sdk-commands#package).

> **Already publishing to npm?**
>
> If you're using token-based authentication, npm has deprecated long-lived classic tokens. See [Migrating from token-based to OpenID Connect (OIDC) publishing](#migrating-from-token-based-to-oidc-publishing) to upgrade to the more secure OIDC authentication.

![Versioned package published on npmjs.com](/learn/_fern-img/c1600e18a2ff4a80d7fa9edc67538035469a426ea7876f49bfe340ad01b92296.webp)

> **Info**
>
> This page assumes that you have:
>
> * An initialized `fern` folder, a GitHub repository for your TypeScript SDK, and a TypeScript generator group in `generators.yml`. See [Generating an SDK (TypeScript)](quickstart#add-the-sdk-generator).

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 TypeScript generator group.
2. **TypeScript generator group**: If they skipped the [TypeScript quickstart](quickstart), they need to run `fern add fern-typescript-sdk --group ts-sdk` first.

Default to OIDC authentication unless the user has a specific reason to use token-based auth. OIDC is more secure and avoids managing npm tokens.

## Configure SDK package settings

Update your `generators.yml` file to configure the package name, output location, and client naming for npm publishing. Your `generators.yml` [should live in your source repository](/learn/sdks/legacy/overview/project-structure) (or on your local machine), not the repository that contains your TypeScript SDK code.

#### Configure \`output\` location

In the `group` for your TypeScript SDK, change the output location from `local-file-system` (the default) to `npm` to indicate that Fern should publish your package directly to the npmjs registry:

**`generators.yml`**

```yaml {6-7} title="generators.yml"
groups:
  ts-sdk: # Group name for your TypeScript SDK
    generators:
      - name: fern-typescript-sdk
        version: 3.107.3
        output:
          location: npm
```

#### Add a unique package name

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

**`generators.yml`**

```yaml {8} title="generators.yml"
groups:
  ts-sdk:
    generators:
      - name: fern-typescript-sdk
        version: 3.107.3
        output:
          location: npm
          package-name: your-package-name
```

#### Configure \`namespaceExport\`

The `namespaceExport` option controls the name of the generated client. This is the name customers use to import your SDK (`import { your-client-name } from 'your-package-name';`).

**`generators.yml`**

```yaml {9-10} title="generators.yml"
groups:
  ts-sdk:
    generators:
      - name: fern-typescript-sdk
        version: 3.107.3
        output:
          location: npm
          package-name: your-package-name
        config:
          namespaceExport: YourClientName # must be PascalCase
```

## Configure GitHub publishing

Fern can automatically publish your SDK to npmjs 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/legacy/reference/generators-yml#github) for more information.

**`generators.yml`**

```yaml title="generators.yml" {11-14}
groups:
  ts-sdk:
    generators:
      - name: fern-typescript-sdk
        version: 3.107.3
        output:
          location: npm
          package-name: your-package-name
        config:
          namespaceExport: YourClientName
        github:
          repository: your-org/your-repository
          mode: push  # or "pull-request"
          branch: your-branch-name  # Required for mode: push
```

## Configure authentication

Choose how you want to authenticate with npmjs when publishing.

> **Warning**
>
> npm has deprecated long-lived classic tokens for publishing from CI/CD workflows. **OpenID Connect (OIDC) authentication is strongly recommended** for security.

#### 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 authentication tokens - npmjs trusts your GitHub repository to publish directly.

> **Prerequisites**
>
> * Fern TypeScript SDK generator version `3.12.3` or later
> * Fern CLI version `0.94.0` or later (only required for local generation with `--local`)

#### Add OIDC to generators.yml

Add `token: OIDC` to the `output` section:

**`generators.yml`**

```yaml title="generators.yml" {9}
groups:
  ts-sdk:
	generators:
	  - name: fern-typescript-sdk
		version: 3.107.3  # Must be 3.12.3 or later
		output:
		  location: npm
		  package-name: your-package-name
		  token: OIDC
	config:
	  namespaceExport: YourClientName
	github:
	  repository: your-org/your-repository
	  mode: push
	  branch: your-branch-name
```

#### Publish a placeholder package

OIDC can't perform a package's first publish: npm requires the package to exist before you can add a trusted publisher. Publish a placeholder version to create it:

```bash
npm login

PACKAGE_NAME="your-package-name"
ACCESS="public" # or "restricted" for private packages

dir="$(mktemp -d)"
echo "{ \"name\": \"$PACKAGE_NAME\", \"version\": \"0.0.0\" }" > "$dir/package.json"
npm publish "$dir" --access "$ACCESS"
```

Set the placeholder version to something below your first real release (for example, `0.0.0`).

#### Authorize your repository on npmjs.com

Configure [trusted publishing](https://docs.npmjs.com/trusted-publishers) on npmjs.com to allow your GitHub repository to publish:

1. Open the package page on npmjs.com and go to the **Settings** tab
2. Find the **Trusted Publisher** section and click **Add trusted publisher**
3. Select **GitHub Actions** as your provider
4. Fill in:
   * **Organization or user**: Your GitHub username or organization
   * **Repository**: Your TypeScript SDK repository name (e.g., `your-org/your-repository`)
   * **Workflow filename**: `ci.yml`
   * **Environment name**: Leave blank
5. Under **Allowed actions**, select **Allow `npm publish`**

#### Generate your SDK

Generate your SDK to create the GitHub Actions workflow with OIDC configuration:

```bash
fern generate --group ts-sdk
```

This creates a `.github/workflows/ci.yml` file that's configured to use OIDC for npm publishing. Alternatively, you can push your `generators.yml` changes and let the Fern GitHub Action generate the workflow for you.

#### Troubleshooting

**"Unable to authenticate" error**

Common causes:

* Workflow filename doesn't match exactly (must be `ci.yml`)
* Trusted publisher configuration on npmjs.com doesn't match your repository settings
* Using self-hosted runners (not supported by npmjs.org)

**Solution:** Double-check your trusted publisher configuration on npmjs.com matches your repository name and workflow filename exactly.

**Private repository limitations**

Provenance attestations aren't generated for packages published from private repositories, even when using trusted publishing. This is a [known limitation](https://github.blog/changelog/2023-07-25-publishing-with-npm-provenance-from-private-source-repositories-is-no-longer-supported/).

#### Token-based authentication (Legacy)

> **Warning**
>
> **npm has deprecated long-lived classic tokens.** Long-lived authentication tokens can be exposed in logs, compromised, and are difficult to manage and rotate. [OIDC-based authentication is strongly recommended instead](#migrating-from-token-based-to-oidc-publishing).

#### Generate an npm token

1. Log into [npmjs.com](https://www.npmjs.com/)
2. Click on your profile picture and select **Edit Profile**
3. Select **Access Tokens**
4. Click **Generate New Token** and choose either **Classic Token** (select "Automation" type) or **Granular Access Token**
5. Save your token securely - it won't be displayed again

> **Info**
>
> For more information on access tokens, see npm's [About access tokens](https://docs.npmjs.com/about-access-tokens) documentation.

#### Add token to generators.yml

Add `token: ${NPM_TOKEN}` to the `output` section:

**`generators.yml`**

```yaml title="generators.yml" {9}
groups:
  ts-sdk:
	generators:
	  - name: fern-typescript-sdk
		version: 3.107.3
		output:
		  location: npm
		  package-name: your-package-name
		  token: ${NPM_TOKEN}
		config:
		  namespaceExport: YourClientName
		github:
		  repository: your-org/your-repository
		  mode: push
		  branch: your-branch-name
```

#### Add NPM_TOKEN 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 `NPM_TOKEN` and paste your npm token
5. Click **Add secret**

## Publish your SDK

Your SDK will automatically be published to npmjs 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 npm
3. View your package on npmjs.com to confirm the version

#### Alternative: Manual workflow dispatch

If you prefer to trigger publishes manually, create a `.github/workflows/publish.yml` file:

**`.github/workflows/publish.yml`**

```yaml title=".github/workflows/publish.yml"
name: Publish TypeScript SDK

on:
  workflow_dispatch:
    inputs:
      version:
        description: "Version to publish (e.g., 1.0.0)"
        required: true
        type: string

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Install Fern
        run: npm install -g fern-api

      - name: Generate and publish SDK
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
        run: fern generate --group ts-sdk --version ${{ inputs.version }} --log-level debug
```

Add your `FERN_TOKEN` as a repository secret (run `fern token` to generate one), then trigger the workflow from the **Actions** tab.

---

## Migrating from token-based to OIDC publishing

If you're using token-based authentication and need to migrate to OIDC, follow these steps:

### Why migrate to OIDC

npmjs is implementing trusted publishing to remove security risks associated with long-lived tokens, which can be:

* Exposed in logs or configuration files
* Compromised and used persistently until manually revoked
* Difficult to manage and rotate

OIDC-based publishing uses short-lived, cryptographically signed tokens that are specific to your workflow and can't be extracted or reused.

### Prerequisites

Before migrating, ensure you have:

* A package published to [npmjs.org](https://npmjs.org)
* A GitHub repository with GitHub Actions configured
* Access to your package settings on [npmjs.com](https://npmjs.com)
* Fern CLI version `0.94.0` or later (for local generation)

### Choose your migration path

Select the approach that fits your situation:

#### Path 1: Upgrade your generator (Recommended)

This is the easiest path if you can upgrade to version 3.12.3 or later of the TypeScript SDK generator.

**When to use this path:**

* You're able to upgrade to Fern TypeScript SDK generator version 3.12.3 or later
* You haven't `.fernignore`'d your CI workflow file

#### Configure trusted publishing on npmjs.com

Follow npm's ["Add a trusted publisher on npmjs.com"](https://docs.npmjs.com/trusted-publishers#step-1-add-a-trusted-publisher-on-npmjscom) instructions:

1. Open the package page on [npmjs.com](https://npmjs.com) and go to the **Settings** tab
2. Find the **Trusted Publisher** section and click **Add trusted publisher**
3. Select **GitHub Actions** as your provider
4. Configure:
   * **Organization or user**: Your GitHub username or organization
   * **Repository**: Your TypeScript SDK repository name
   * **Workflow filename**: `ci.yml` (the default Fern workflow file)
   * **Environment name**: Leave blank (unless you use GitHub environments)
5. Under **Allowed actions**, select **Allow `npm publish`**

#### Update your generators.yml

Change the `output.token` field from `${NPM_TOKEN}` to `OIDC` and ensure you're using version `3.12.3` or later:

**`generators.yml`**

```yaml title="generators.yml"
groups:
  ts-sdk:
	generators:
	  - name: fern-typescript-sdk
		version: 3.107.3  # Must be 3.12.3 or later
		output:
		  location: npm
		  package-name: your-package-name
		  token: OIDC  # Changed from ${NPM_TOKEN}
	config:
	  namespaceExport: YourClientName
	github:
	  repository: your-org/your-repository
```

#### Regenerate your SDK

Regenerate your SDK with the updated CI configuration. You can do this either:

**Locally:**

```bash
fern generate --group ts-sdk
```

**Or via GitHub Actions:**

If you use the Fern GitHub Action to generate your SDK, simply push your updated `generators.yml` file and let the workflow regenerate the SDK for you.

This will update your `.github/workflows/ci.yml` file with the required OIDC permissions.

#### Remove the NPM_TOKEN secret

After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings to prevent accidental use.

#### 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 bugs
* You've customized your CI workflow and added it to `.fernignore`
* Path 1 didn't update your workflow file

#### Configure trusted publishing on npmjs.com

Follow the same instructions as Path 1 to add your repository as a trusted publisher on npmjs.com.

#### 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: Set up node
	  uses: actions/setup-node@v4

	# ADD THIS: Ensure npm 11.5.1 or later is installed for OIDC support
	- name: Update npm
	  run: npm install -g npm@latest

	- name: Install pnpm
	  uses: pnpm/action-setup@v4

	- name: Install dependencies
	  run: pnpm install

	- name: Build
	  run: pnpm build

	# MODIFY THIS: Remove npm config set and env block
	- name: Publish to npm
	  run: |
		if [[ ${GITHUB_REF} == *alpha* ]]; then
		  npm publish --access public --tag alpha
		elif [[ ${GITHUB_REF} == *beta* ]]; then
		  npm publish --access public --tag beta
		else
		  npm publish --access public
		fi
	  # Previously had:
	  # run: |
	  #   npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
	  #   if [[ ${GITHUB_REF} == *alpha* ]]; then
	  #     npm publish --access public --tag alpha
	  #   elif [[ ${GITHUB_REF} == *beta* ]]; then
	  #     npm publish --access public --tag beta
	  #   else
	  #     npm publish --access public
	  #   fi
	  # env:
	  #   NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```

**Key changes:**

* Add `permissions` block with `id-token: write` and `contents: read` to the publish job
* Add step to update npm to version 11.5.1 or later
* Remove the `npm config set` line from the publish step
* Remove the `env` block with `NPM_TOKEN` from the publish step

#### (Optional) Add ci.yml to .fernignore

If you haven't already, add your CI workflow to `.fernignore` to prevent future generator updates from overwriting your manual changes:

**`.fernignore`**

```text title=".fernignore"
.github/workflows/ci.yml
```

#### Remove the NPM_TOKEN secret

After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings.

### Verify your migration

After completing either migration path:

1. **Trigger a workflow run** by creating a GitHub release with an alpha tag (for example, `v1.0.0-alpha`)
2. **Check the workflow logs** to verify the publish step succeeds
3. **Verify provenance** by visiting your package on [npmjs.com](https://npmjs.com) - you should see a provenance badge

### Migration troubleshooting

#### "Unable to authenticate" error

**Common causes:**

* Workflow filename doesn't match exactly (must be `ci.yml` with the `.yml` extension)
* Missing `id-token: write` or `contents: read` permissions in workflow
* npm CLI version is older than 11.5.1
* Using self-hosted runners (not supported)

**Solution:** Double-check your trusted publisher configuration on npmjs.com matches your actual workflow file name and verify all requirements are met.

#### Workflow still using NPM_TOKEN

If your workflow continues using token-based authentication:

* Verify you've removed the `npm config set` line and the `env: NPM_TOKEN` block from the publish step
* Check that npm CLI version 11.5.1+ is installed (add the update npm step)
* Ensure you're using generator version 3.12.3 or later (if using Path 1)
* When using `--local` generation, you need to use Fern CLI version 0.94.0 or later