Publish checklist
Most "tode shows nothing" reports come from the published build, not from development: a domain missing from the manifest, a key that was not in .env when the bundle was built, or a plugin that was never registered. Go through the five steps in order before you press Publish; each one is a minute.
1. The manifest
Figma reads manifest.json from the project root. These fields decide whether the SDK can talk to tode at all.
{
"name": "Font Replacer",
"id": "1100808562040625391",
"api": "1.0.0",
"main": "dist/main.js",
"ui": "dist/ui.html",
"editorType": ["figma", "figjam", "slides"],
"documentAccess": "dynamic-page",
"permissions": ["currentuser"],
"networkAccess": {
"allowedDomains": [
"https://processing.usetode.dev",
"wss://processing.usetode.dev"
],
"reasoning": "Usage analytics (tode). No document content is sent."
}
}| Field | What tode needs | What goes wrong without it |
|---|---|---|
networkAccess.allowedDomains | Both entries: https://processing.usetode.dev for events and wss://processing.usetode.dev for sessions | Figma blocks the request. The SDK logs it and the plugin keeps working, but nothing reaches the dashboard. This is the most common cause. |
permissions | "currentuser" | figma.currentUser is null. Events arrive, but every one is anonymous: no unique users, no retention, no stickiness. |
id | The numeric id Figma assigned to this plugin | A plugin published with another plugin's id, or with a placeholder, fails Figma's validation. See step 2. |
documentAccess | "dynamic-page" | Required by Figma for every new plugin. The template sets it; keep it. |
editorType | The editors the plugin should appear in: figma, figjam, dev, slides, buzz | Figma does not accept ["figjam", "dev"]. |
main, ui | Paths relative to the manifest, pointing at the production build | A path that points at a source file or at a missing build publishes a broken plugin. |
reasoning is optional unless allowedDomains includes * or localhost; Figma shows the allowed domains on the Community page either way, so the sentence is worth having. devAllowedDomains is for local servers during development and must not carry the tode hosts; they belong in allowedDomains so the published build has them.
Projects from the template ship this manifest and npm run check fails if any of these entries goes missing.
2. The id
Figma assigns the id. You get it in one of two places:
- Plugins → Development → New plugin in Figma Desktop writes a manifest with a fresh id. Copy that id into your own
manifest.jsonif you already have a project. - Publish assigns one at the time of publishing if the manifest did not have a real one yet. The template ships
"id": "0000000000000000000"as a placeholder: development works with it, and you replace it with the id Figma shows in the publish dialog.
One id, one plugin. Reusing the manifest of another plugin publishes an update to that plugin, not a new one.
tode ties events to the API key, not to this id, so a wrong id does not route events elsewhere. The id matters for Figma, and for the next step: the Community URL contains it.
3. The key in the build
The SDK reads the key from tode.init({ apiKey }) in the controller. In a template project the key comes from TODE_API_KEY in .env, which the bundler inlines at build time as __TODE_API_KEY__. That means:
- The key has to be in
.envwhen you runnpm run build. A build on a machine without.env, or in CI, ships a plugin with analytics off; the controller prints[tode] TODE_API_KEY is empty, analytics is offand nothing else happens. - Build locally with
.envin place, then publish from that build. - The key ends up in the bundle. That is expected: it is a public identifier tied to one plugin, rate-limited per IP and per user, and you can rotate it in the dashboard (the old key keeps working for 24 hours).
To get the key, register the plugin in the dashboard: paste its Community URL (https://www.figma.com/community/plugin/<id>/...). tode reads the id and the name from the URL and issues the key. If the plugin is not on Community yet, publish it first with the key empty, register it, add the key, rebuild, and publish an update.
4. Publish
Publishing happens in Figma Desktop only: Plugins → Manage plugins → the plugin's menu → Publish. Before that:
npm run check # typecheck, lint, tests, production build, manifest checkThe publish form asks for a name, a tagline, a description and a category, an icon (128×128) and a thumbnail (1920×1080). Figma derives the network access label from the manifest; a plugin without networkAccess is listed as "Unknown network access". Public listings go through review, which can take days; organisation-only plugins do not.
An update is the same flow: rebuild, Publish → publish an update. Editing the listing text does not need a new build.
5. Verify
Run the published plugin once from Community, not from the development entry, and check:
- The dashboard shows the session. Sessions per day gets a bar within a minute of the plugin UI opening. If not, the WebSocket host is missing from
allowedDomains. - An action arrives. Trigger one tracked feature. Actions per day shows it. If sessions arrive and actions do not,
init()is not being called or the key is empty in the build. - Unique users is not zero. If it is,
currentuseris missing frompermissions.
With debug: true in init() the SDK logs each call and each server answer to the plugin console (Plugins → Development → Open console), which turns any of the three into a one-line diagnosis. Turn it off for the release build. The Error Handling page lists every server response code.
Before you press Publish
allowedDomainshas bothprocessing.usetode.deventries.permissionshascurrentuser.idis the one Figma gave this plugin..envholds the key and the build is fresh.npm run checkexits 0.- One run of the production build shows a session and an action on the dashboard.