Publishing to Edge Add-ons from CI

Automate Microsoft Edge Add-ons releases with the Partner Center publish API: API credentials, uploading a package, polling operation status, submitting with release notes, handling errors, and adding Edge to a multi-store pipeline.

Published October 2, 2026 Updated October 2, 2026 7 min read
Table of Contents

The release pipeline uploads to the Chrome Web Store and AMO automatically, and then someone has to log in to Microsoft Partner Center, upload the same ZIP, paste the release notes and click Publish — every release, usually a day late. Edge Add-ons has a REST API for exactly this: upload a package to an existing product, poll until it is processed, then submit it for certification. The API is asynchronous and status-driven in a way the Chrome Web Store API is not, so a script that fires and forgets reports success for releases that actually failed. This guide builds a robust Edge publishing step. It belongs to CI and release automation.

How the Edge Add-ons API works

The API updates an existing product — the first submission must be made manually in Partner Center, which assigns the product ID. Authentication uses API credentials generated in Partner Center (the current v1.1 API uses a Client ID and an API key sent as headers). Publishing is two asynchronous operations: upload a package to the product’s draft (POST …/products/{productId}/submissions/draft/package), which returns an operation ID in the Location header that you poll until it reports Succeeded or Failed; then publish the draft (POST …/products/{productId}/submissions) with optional notes for certification, which also returns an operation to poll. Certification then happens on Microsoft’s side, typically within days.

The two-operation publish flowCI uploads the ZIP to the draft and receives an operation id; polls until the upload succeeds; submits the draft with notes for certification and receives another operation id; polls until the submission is accepted; certification then proceeds in Partner Center.CI jobEdge Add-ons APICertificationPOST draft/package (zip)202 + Location: operationIdGET operation → InProgress … SucceededPOST submissions {notes}GET operation → Succeededin certification
Two async operations — poll both, or you don't know it worked.

Step-by-step: an Edge publish script

1. Create API credentials and store them as secrets

In Partner Center, open the Edge program’s Publish API page and create API credentials. Store the Client ID, API key and the product ID as CI secrets (for example EDGE_CLIENT_ID, EDGE_API_KEY, EDGE_PRODUCT_ID). Keys expire; record the expiry date and rotate before it. See managing store credentials in CI secrets.

2. Upload the package

 1// scripts/publish-edge.mjs
 2import { readFile } from "node:fs/promises";
 3
 4const BASE = "https://api.addons.microsoftedge.microsoft.com/v1/products";
 5const { EDGE_CLIENT_ID, EDGE_API_KEY, EDGE_PRODUCT_ID } = process.env;
 6const headers = { "Authorization": `ApiKey ${EDGE_API_KEY}`, "X-ClientID": EDGE_CLIENT_ID };
 7
 8async function upload(zipPath) {
 9  const res = await fetch(`${BASE}/${EDGE_PRODUCT_ID}/submissions/draft/package`, {
10    method: "POST",
11    headers: { ...headers, "Content-Type": "application/zip" },
12    body: await readFile(zipPath),
13  });
14  if (res.status !== 202) throw new Error(`Upload failed: ${res.status} ${await res.text()}`);
15  return res.headers.get("Location");          // operation id
16}

Execution context: Node in CI. The request body is the raw ZIP built for Edge — usually the same package as Chrome, since Edge is Chromium-based. A 202 Accepted means processing has started, not finished. Check the current API documentation for the exact header names and base URL for your API version; Microsoft has revised authentication between versions.

Operation statuses and what CI should doStatuses returned when polling Edge Add-ons upload and publish operations, what each means, and the CI action to take.StatusMeansCI actionInProgressStill processingWait and poll againSucceededStep completeContinueFailedRejected (see errors)Fail job, print errorsHTTP 429 / 5xxThrottled or transientBack off and retry
Only Succeeded is success; everything else needs handling.

3. Poll the operation with backoff

 1async function waitFor(url, label, { timeoutMs = 10 * 60_000 } = {}) {
 2  const start = Date.now();
 3  let delay = 5000;
 4  while (Date.now() - start < timeoutMs) {
 5    const res = await fetch(url, { headers });
 6    if (res.status === 429 || res.status >= 500) { await sleep(delay *= 2); continue; }
 7    const body = await res.json();
 8    if (body.status === "Succeeded") return body;
 9    if (body.status === "Failed") {
10      throw new Error(`${label} failed: ${body.message ?? ""}\n${JSON.stringify(body.errors ?? [], null, 2)}`);
11    }
12    await sleep(delay = Math.min(delay * 1.5, 30_000));
13  }
14  throw new Error(`${label} timed out after ${timeoutMs / 1000}s`);
15}
16const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

Execution context: Node in CI. Package validation can take from seconds to minutes. Exponential backoff with a cap keeps the polling polite, and a timeout prevents a stuck job from running for hours. Printing the errors array makes failures actionable — common ones are a version not higher than the published one, or manifest validation errors.

4. Submit for certification

 1async function submit(notes) {
 2  const res = await fetch(`${BASE}/${EDGE_PRODUCT_ID}/submissions`, {
 3    method: "POST",
 4    headers: { ...headers, "Content-Type": "application/json" },
 5    body: JSON.stringify({ notes }),
 6  });
 7  if (res.status !== 202) throw new Error(`Submit failed: ${res.status} ${await res.text()}`);
 8  return res.headers.get("Location");
 9}
10
11const uploadOp = await upload(process.argv[2]);
12await waitFor(`${BASE}/${EDGE_PRODUCT_ID}/submissions/draft/package/operations/${uploadOp}`, "Upload");
13const submitOp = await submit(`Release ${process.env.VERSION}. See changelog: ${process.env.CHANGELOG_URL}`);
14await waitFor(`${BASE}/${EDGE_PRODUCT_ID}/submissions/operations/${submitOp}`, "Submit");
15console.log("Edge submission accepted for certification");

Execution context: Node in CI. The notes field is for certification testers — explain anything they need to test (test accounts, how to trigger a feature) and link the changelog. A submission fails if another submission is already in certification; the error says so, and the job should fail clearly rather than retrying.

Edge in a multi-store release jobA tagged release builds once, then runs store steps in sequence: Chrome Web Store upload and publish, AMO sign and submit, Edge upload, poll, submit and poll; each step reports its own status, and a failure in one store does not hide success in another.Tag v2.4.0release workflowBuild + zipreproducibleArtifactschrome.zip, firefox.zippublish jobsChrome Web Storeupload + publishAMOsign + submitEdge Add-onsupload, poll, submit, poll
Build once, publish to each store, report each result.

5. Add it to the release workflow

 1# .github/workflows/release.yml (excerpt)
 2  publish-edge:
 3    needs: build
 4    runs-on: ubuntu-latest
 5    environment: edge-store
 6    steps:
 7      - uses: actions/checkout@v4
 8      - uses: actions/setup-node@v4
 9        with: { node-version-file: .nvmrc }
10      - uses: actions/download-artifact@v4
11        with: { name: packages, path: release }
12      - run: node scripts/publish-edge.mjs release/extension-chrome-${{ github.ref_name }}.zip
13        env:
14          EDGE_CLIENT_ID: ${{ secrets.EDGE_CLIENT_ID }}
15          EDGE_API_KEY: ${{ secrets.EDGE_API_KEY }}
16          EDGE_PRODUCT_ID: ${{ secrets.EDGE_PRODUCT_ID }}
17          VERSION: ${{ github.ref_name }}
18          CHANGELOG_URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}

Execution context: GitHub Actions. A separate job per store means an Edge failure doesn’t block or mask the Chrome publish, and can be re-run alone. A protected environment can require manual approval and restricts who can use the secrets. See building a GitHub Actions pipeline for extensions.

6. Keep Edge’s listing in sync

The API updates the package and submission, not the listing text or images. When the store description or screenshots change, update them in Partner Center manually (or note it in the release checklist). See publishing to Microsoft Edge Add-ons.

7. Handle the version rule

Edge requires each upload’s version to be higher than the last published one. If an upload fails for a version you already published elsewhere, you need a new version — keep versions in lockstep across stores to avoid confusion. See versioning and changelogs for extension releases.

Common mistakes

  • Treating 202 as success. Poll the operation.
  • No timeout on polling. Jobs hang for hours.
  • Retrying a submission already in certification. Fails repeatedly; report and stop.
  • Expired API keys. Releases fail on the day you need them.
  • One job for all stores. One failure hides the others.

Cross-browser variation

Verification

  1. Run the script against a test product (or with a deliberately old version) and confirm a Failed status fails the job with readable errors.
  2. Run a real release and confirm the submission appears as “In certification” in Partner Center.
  3. Remove a secret and confirm the job fails before uploading.
  4. Confirm the Edge job can be re-run alone.

FAQ

Can the API create a new product?

No. Create the product and first submission in Partner Center; the API handles updates.

How long does certification take?

Typically up to several business days; the API only confirms submission.

Can I publish to a subset of markets or as a draft?

Market and visibility settings are managed in Partner Center; the API submits the draft with the existing settings.

Can I check certification status from CI later?

The publish operation reports acceptance, not certification results. Check Partner Center or its email notifications for the outcome, and keep the operation IDs in the job log so support requests can reference them.

Other Testing, Debugging & Performance Optimization Resources