Firefox Add-ons
Firefox submissions go through the AMO API v5. Reach for this page when you set up the AMO credentials, choose between a listed and an unlisted channel, or need the source archive and data collection declaration a review asks for.
Quick start
curl -s -X POST https://www.extension.dev/api/cli/stores/submit \
-H "Authorization: Bearer $EXTENSION_DEV_TOKEN" \
-H "Content-Type: application/json" \
-d '{"browsers":["firefox"],"buildSha":"<sha>","channel":"stable"}'The platform creates an upload, polls its validation, then creates the version from it. The same submission starts from the console's Submissions tab or from an agent with extension_submit.
How it works
Credentials
AMO authenticates with a JWT signed from an issuer and a secret. The platform mints the JWT per request with a 30 second expiry, so the secret never travels and a captured token dies fast. Add the key and secret in the console under Project settings, Store publishing, Firefox.
Listed and unlisted
AMO has its own channel concept, separate from Extension.dev release channels. The AMO channel is the Automated release channel setting in the project's Firefox store settings, and it defaults to unlisted. A submission with no channel saved goes unlisted, never listed.
unlisted is how to get a signed build for self distribution without a public listing.
The first submission
A first submission cannot be listed without an existing add-on GUID. It either goes unlisted or creates a new add-on. When AMO assigns the GUID, the platform writes it back to the project's Firefox settings, so the second submission already knows its add-on.
Source code uploads
AMO rejects a listed review of a bundled or minified build that carries no sources, and every build the platform produces is bundled. Set Source archive to Attach the repository at the built commit in the project's Firefox store settings. Every Firefox submission then carries a zip of the source repository at the commit that produced the build.
The archive is packaged during the submission and attached to the version for Mozilla reviewers. The setting is off until you turn it on.
Reviewers also need the steps that turn that source into the submitted build. Those come from STORE.md, which is submitted as the reviewer note.
The console's readiness checklist warns when a project has no archive configured, on either channel: a listed review rejects a version without sources, and an unlisted version is signed unread but Mozilla's policies still apply to it, so a reviewer can ask for the sources or block the add-on later. The dry run on the submit call does not read this setting. The warning is advisory. AMO is what rejects.
Reviewers rebuild from the archive on their own machine, with Node 24 on Ubuntu unless the note says otherwise, so the reviewer note in STORE.md names the Node version the platform built with (22 unless the project set one), the install command against the lockfile, the build command and the output directory.
Data collection consent
AMO requires every add-on to declare data collection permissions in the manifest, under browser_specific_settings.gecko.data_collection_permissions. An extension that collects no data still declares {"required": ["none"]}. AMO rejects a new add-on without the key, and Mozilla has announced that every existing extension must adopt it during 2026. The data collection permissions documentation on Extension Workshop has the shape.
The preflight reads the key from the Firefox build under submission (manifest.json inside its stored zip). When that build left no readable artifact, it reads the source manifest at the build's commit with the Extension.js firefox: and gecko: prefixes resolved the way the build resolves them, and the check names the file it read either way.
The platform warns twice. A dry run on any project targeting Firefox returns a FIREFOX_DATA_COLLECTION_PERMISSIONS warning, and the submission logs a warning when the uploaded package's manifest.json is missing the key. Both are advisory. AMO is what rejects.
The one refusal is a package whose manifest.json cannot be read out of the zip at all. That submission stops with FIREFOX_DATA_COLLECTION_UNVERIFIED before anything is uploaded.
Reviewer notes, release notes and category
Firefox reviewer notes and release notes are read from STORE.md in the project root and submitted automatically. See Publishing.
The - Category: line under ## Listing is shared by every store, and AMO has fewer categories than the Chrome Web Store: it has no Accessibility and no Productivity. When the shared name is one AMO lacks, put a - Category: line under ## Firefox Add-ons with one of AMO's own names; Other is the closest when none fits. The per-store line wins for Firefox and leaves the other stores on the shared one.
An unlisted upload needs no category. AMO signs the file for self distribution and the listing is never sent, so the preflight only warns. A listed submission is refused by AMO after the file is sent when the add-on has no AMO category, so there the preflight blocks until STORE.md names one.
Watching
The Submissions tab follows the submission to a terminal state, and GET /api/projects/{id}/stores/status returns the same. The states are the eight on Publishing. Firefox tracks by the version id the submission returns.
Reference
Secrets
| Value | Secret | Notes |
|---|---|---|
| API key (issuer) | STORE_FIREFOX_API_KEY | From the AMO API credentials page |
| API secret | STORE_FIREFOX_API_SECRET | Shown once, at creation |
Store settings
| Setting | Stored as | Values |
|---|---|---|
| Add-on GUID | storeId | Empty for a first unlisted submission, written back by the platform |
| Automated release channel | releaseChannel | unlisted (default) or listed |
| Source archive | sourceArchive | none (default) or repository |
AMO channels
| AMO channel | Result |
|---|---|
listed | Published on addons.mozilla.org, goes through review |
unlisted | Signed and returned to you, not listed publicly |
Warnings and refusals
| Code | Kind | Meaning |
|---|---|---|
FIREFOX_DATA_COLLECTION_PERMISSIONS | Warning when the Firefox manifest of the build lacks it | Declare data_collection_permissions in the manifest; the warning names the file it read |
FIREFOX_DATA_COLLECTION_UNVERIFIED | Refusal before upload | manifest.json could not be read out of the zip |
When it fails
- Validation fails. Validation failures surface while the upload is polled, before a version exists. Read the validation messages. The version was never created.
- A first submission asks for
listed. It is refused by design. Submit unlisted or let it create the add-on, then go listed once the GUID exists. - A store is blocked. The preflight,
dryRunon the submit call, reports credential health and the console page that fixes a blocked store. - The submission stops with
FIREFOX_DATA_COLLECTION_UNVERIFIED. The package'smanifest.jsoncould not be read. Rebuild the package and submit again.
Next
- Publishing for
STORE.mdand dry runs - HTTP API for the submit and status endpoints