Skip to main content
Automated Chrome Web Store submissions need two identifiers and one credential set: You need either the OAuth trio or the service account, not both. When both are configured, the service account wins.

Prerequisites

The Chrome Web Store API cannot create a new item. The first upload of a new extension must happen by hand in the Developer Dashboard. The 32-character extension ID only exists after that upload.
  1. Register a developer account at the Chrome Web Store Developer Dashboard. Registration has a one-time $5 fee.
  2. Build a store zip (npx extension build --browser chrome --zip).
  3. Upload that zip manually in the dashboard to create the item.
  4. Copy the extension ID from the item’s dashboard URL.
  5. Copy the publisher ID: it is the UUID in your dev console URL, chrome.google.com/webstore/devconsole/<UUID>.

The easy path: npx extension-deploy init

The wizard in @extension.dev/deploy acquires and verifies every Chrome value, and it mints the refresh token for you through a local loopback consent flow:
It prompts for the extension ID, the publisher ID, and your OAuth client ID and secret. It then opens the Google consent page, receives the authorization code on a local 127.0.0.1 receiver, exchanges it for a refresh token, verifies the result against the live Chrome Web Store API, and writes everything to .env.submit. Before you run it, create the OAuth client (steps 1 to 3 below). The wizard handles the rest.
Do not use the Google OAuth Playground to mint the refresh token. The Playground requires a Web application client with its redirect URI, and the Chrome Web Store flow requires a Desktop app client. Combining them fails with redirect_uri_mismatch. The init wizard’s loopback flow is the supported path for Desktop app clients.

Create the OAuth client

  1. Create or choose a project in the Google Cloud Console.
  2. Enable the Chrome Web Store API for that project.
  3. On the credentials page, create an OAuth client ID with application type Desktop app. A Web application client does not work here.
  4. Copy the client ID and the client secret.
  5. Run npx extension-deploy init to mint the refresh token, as described above. The token authorizes the https://www.googleapis.com/auth/chromewebstore scope.
If your OAuth consent screen is in Testing status, Google revokes the refresh token after 7 days. Your first submission works and the credential dies a week later. Publish the consent screen (In production), or use a service account, which has no such expiry.

Alternative: a service account

A Google Cloud service account avoids OAuth consent entirely and is the most stable choice for CI:
  1. In the Google Cloud Console, create a service account in the same project that has the Chrome Web Store API enabled.
  2. Create a JSON key for it and download the file.
  3. In the Chrome Web Store dev console, open Account and add the service account’s email address to your publisher. One service account per publisher.
Provide the JSON key content (or a path to the file) as the credential. When a service account is configured, it is preferred over the OAuth trio.

Blast radius and rotation

Chrome credentials are publisher-wide, not per extension. A token that can publish one item can publish every item under the same publisher account. Treat the refresh token and the service account key accordingly, and prefer a separate publisher account per client if you manage extensions for others.
To rotate, mint a new refresh token with npx extension-deploy init (or a new service account key) and re-enter it wherever it is stored. Saved platform secrets are write-only, so rotation always means re-entering the value.

Name map

The same values wear different names on each surface:

Listing metadata stays manual

The Chrome Web Store API accepts no listing metadata. Store listing copy, screenshots, and permission justifications are entered in the Developer Dashboard by hand. Keep them in the Chrome section of STORE.md so every resubmission copies from one tracked source.