Publishing a Figma plugin without losing its analytics.
The five things that make a published plugin go quiet on the dashboard: a missing allowed domain, a key that was not in the build, the placeholder id, the currentuser permission, and what Figma rejects outright.
, by Ilia
The plugin works on my machine, I publish it, and the dashboard stays empty. I have been on both ends of that email. Every time, the cause was one of five things, and none of them shows up in development, because development is the one place where Figma is lenient and the key is always in the right file. This is the list I go through before I press Publish now.
The domain Figma will not let you call
A published plugin can only talk to the hosts named in networkAccess.allowedDomains. Anything else is refused by Figma before the request leaves the plugin. The SDK notices, writes one line to the console and carries on, because a plugin must never break over analytics. So the plugin feels fine and the dashboard sees nothing.
tode needs two entries, not one: the events go over HTTPS and the sessions go over a WebSocket, and Figma treats wss:// as its own scheme.
"networkAccess": {
"allowedDomains": [
"https://processing.usetode.dev",
"wss://processing.usetode.dev"
],
"reasoning": "Usage analytics (tode). No document content is sent."
}If only the first line is there, actions arrive and sessions do not. If only the second, the other way round. The reasoning sentence is optional, but Figma prints the allowed domains on the Community page, and a plugin that says why it phones home reads better than one that does not. One trap: devAllowedDomains is for local servers while you develop and is ignored in the published build. The tode hosts go in allowedDomains.
The key that was not there when you built
The API key is not read at run time from anywhere. It is a string in the controller, passed to tode.init. In a project from the template it comes from TODE_API_KEY in .env, and the bundler pastes it into the main bundle while building. Which means the key has to exist on the machine, in that file, at the moment you run npm run build.
The way this goes wrong: you build on a laptop that never had .env, or in CI, or after a fresh clone. The build succeeds. The plugin runs. The controller prints [tode] TODE_API_KEY is empty, analytics is off and nobody reads the console of a published plugin. Build the release on the machine that has the key, and look at the bundle once if you are unsure: the key should be in it. That is fine, by the way. The key is a public identifier for one plugin, rate-limited per address and per user, and you can rotate it from the dashboard; the old one keeps working for a day so people on the previous build are not cut off.
The id with nineteen zeros
Figma assigns plugin ids; you never make one up. There are two ways to get one. "New plugin" under Plugins → Development writes a manifest with a fresh id, which you can copy into a project you already have. Or you publish with no real id in the manifest, and the publish dialog shows the id Figma just assigned, which you paste in.
The template ships "id": "0000000000000000000" on purpose. Development does not care. Publishing does: replace it with the id from the dialog, and do not borrow the manifest of another plugin of yours, because the id is what tells Figma which plugin you are updating.
For tode the id is not what routes events; the key is. The id matters because it is in the Community URL, and the Community URL is what you paste into the dashboard to register the plugin and get a key. Which creates an order problem for a brand-new plugin: no Community page, no key. What I do is publish once with the key empty, register the plugin from its new Community URL, put the key in .env, rebuild, and publish an update the same hour. Updates do not go through review again.
The permission that makes everyone the same person
Without "permissions": ["currentuser"], figma.currentUser is null and the SDK sends an empty user id. Events still count, actions still chart, but unique users, returning users, retention and stickiness all read zero, because there is nobody to tell apart. This one is easy to miss precisely because the dashboard is not empty; it is just missing the half you wanted.
Things Figma rejects outright
These are not tode's rules, but they stop a publish or an update:
documentAccessmust be"dynamic-page"for every new plugin. With it, pages load on demand, so the code uses the async forms,figma.getNodeByIdAsyncandloadAllPagesAsync, which the template already does.editorTypeis a list of where the plugin appears:figma,figjam,dev,slides,buzz. Figma does not accept["figjam", "dev"]as a pair.mainanduipoint at the built files, relative to the manifest. A manifest that points atsrc/publishes a plugin that does nothing.- No
networkAccessblock at all gets the listing labelled "Unknown network access", which is worse for installs than naming the one host you use.
The order I publish in
.envhas the key. If the plugin is new and has no key yet, I accept one keyless release and come back.npm run check. In a template project that is the typecheck for both threads, lint, the tests, a production build and a manifest check that fails on a missing domain, a missing permission or a non-numeric id.- Figma Desktop, Plugins → Manage plugins, the plugin's menu, Publish. Name, tagline, description, category, an icon at 128×128, a thumbnail at 1920×1080. Public listings wait for review; updates to an existing listing do not.
- The id from the dialog goes into
manifest.jsonif it was not there, and gets committed.
What to look at afterwards
Run the published plugin from Community, not the development copy, and watch the dashboard for a minute. A session bar means the WebSocket host is allowed. An action means the key is in the build and init ran. A unique-users count above zero means the permission is there. Each of the three failing points at exactly one of the sections above. If you want the SDK to say it out loud, debug: true in init logs every call and every server answer to the plugin console; take it out for the next build.
The same list, as a table with the server codes, is in the docs: Publish checklist. The dashboard where the key comes from is at app.usetode.dev.