Overview#
An add-in that works on your machine is not yet a product. Other people need to install it without editing paths, run it on Windows and macOS, update it, and uninstall it cleanly. This page covers the structure of a well-formed add-in, the manifest, resource handling, code organisation, versioning and compatibility, the main distribution routes, and a checklist for release.
Distribution has three realistic routes: hand someone a zip file and instructions, supply a small installer script, or publish on the Autodesk App Store. Autodesk's documentation describes where each kind of install lands on disk; it says little about the store's submission process, so that part is marked below as not confirmed.
Where to find it#
- Scripts and Add-Ins dialog: where users see and run your add-in, and where the Run on Startup column is set (add-ins only). The + menu links add-ins from any folder and has a shortcut to the Autodesk App Store.
- The folders Fusion searches automatically (all documented):
%appdata%\Autodesk\Autodesk Fusion\API\AddIns(Windows) or$HOME/Library/Application Support/Autodesk/Autodesk Fusion/API/AddIns(Mac) for user add-ins (Autodesk's topic also spells the product folderAutodesk Fusion 360; use Open File Location to confirm);%appdata%\Autodesk\ApplicationPluginsfor App Store apps; and%appdata%\Autodesk\FusionAddinsfor Fusion-specific apps that are not from the store. - Autodesk's Python add-in template, which the Create dialog generates and which shows the recommended structure.
Anatomy of a distributable add-in#
A minimal add-in folder:
MyTool/
MyTool.py
MyTool.manifest
config.py (DEBUG, COMPANY_NAME and other shared variables)
lib/ (the template's fusion360utils event helpers)
commands/
__init__.py (imports each command module and calls its start/stop)
commandDialog/
__init__.py
entry.py
resources/ (icon images)
README.md (your own)
LICENSE (your own)
That layout is the one Autodesk's Python add-in template generates (with commandDialog, paletteShow and paletteSend command folders); the template's MyFirstAddIn.py only calls commands.start() in run and futil.clear_handlers() then commands.stop() in stop.
Rules to remember:
- The folder name, the main
.pyfile name, and the.manifestfile name match; the manifest does not store the name. To rename an item, rename the folder and files. - The main file defines
run(context)andstop(context). - Keep everything the add-in needs, icons included, inside its folder, so it "can be installed by simply copying this folder". Modules go in a subfolder and are imported relatively (
from .Modules import xlrd). - General guidance, not Autodesk data: do not write into the add-in folder at runtime; use a per-user data folder (see below).
The manifest for add-ins#
{
"autodeskProduct": "Fusion",
"type": "addin",
"id": "",
"author": "Jane Maker",
"description": {
"": "Generates parametric brackets from a dialog."
},
"version": "1.2.0",
"runOnStartup": true,
"supportedOS": "windows|mac",
"editEnabled": true,
"iconFilename": "AddInIcon.svg"
}
Field notes:
| Key | Meaning | Notes |
|---|---|---|
autodeskProduct | Fusion in the current topic (Fusion360 in the older one) | Older manifests carry Fusion360 |
type | addin | script for scripts |
id | Not used by Fusion; may be left empty | An older version of the topic called it a GUID reserved for future use |
author | Display name | Shown in the dialog |
description | Locale dictionary keyed by Microsoft language code | Empty key is the default |
version | Any string such as 1.0.0, 2016, R1 | Shown in the dialog's version column |
runOnStartup | Whether Fusion starts it automatically | Default false; the dialog's column also controls it |
supportedOS | windows, mac, or `windows | mac` |
editEnabled | Appears in Autodesk's example | Not described: not confirmed |
iconFilename | Appears in Autodesk's example (AddInIcon.svg) | Not described: not confirmed |
sourcewindows, sourcemac | C++ only: project file opened by Edit |
Cross-check against the manifest the Create dialog generates in your installed version, since the topic itself shows two slightly different examples.
Step by step: prepare a release#
1. Make run and stop symmetrical#
Whatever run creates, stop must remove. Autodesk's UI topic: the add-in "should delete both the command definitions and controls", because otherwise the commands remain visible but dead after the add-in is unloaded. Also remove palettes, event handlers, custom events and any threads. stop is called when the user stops the add-in or when Fusion shuts down (context['IsApplicationClosing']). A reliable test (general guidance): run, stop, run, stop in succession; if the second run raises "already exists", cleanup is incomplete.
import adsk.core, traceback
from .commands import entry
def run(context):
try:
entry.start()
except:
adsk.core.Application.get().userInterface.messageBox(
'Failed to start:\n{}'.format(traceback.format_exc()))
def stop(context):
try:
entry.stop()
except:
adsk.core.Application.get().userInterface.messageBox(
'Failed to stop:\n{}'.format(traceback.format_exc()))
2. Keep user data out of the add-in folder#
import os, json, sys
def user_data_dir(name='MyTool'):
if sys.platform == 'win32':
base = os.environ.get('APPDATA', os.path.expanduser('~'))
else:
base = os.path.expanduser('~/Library/Application Support')
path = os.path.join(base, name)
os.makedirs(path, exist_ok=True)
return path
def load_settings():
p = os.path.join(user_data_dir(), 'settings.json')
if os.path.exists(p):
with open(p, encoding='utf-8') as fh:
return json.load(fh)
return {'last_size_mm': 20}
def save_settings(data):
with open(os.path.join(user_data_dir(), 'settings.json'), 'w', encoding='utf-8') as fh:
json.dump(data, fh, indent=2)
An add-in installed under a protected location, or updated by replacing its folder, must never depend on files it wrote into itself.
3. Reference resources relative to the file#
import os
HERE = os.path.dirname(os.path.abspath(__file__))
ICON_FOLDER = os.path.join(HERE, 'resources')
html_path = os.path.join(HERE, 'palette', 'index.html')
Use os.path.join and avoid hardcoded separators so the same code runs on both operating systems.
4. Handle platform and version differences#
import adsk.core, sys
def app_version():
return adsk.core.Application.get().version # a string describing the Fusion version
IS_MAC = sys.platform == 'darwin'
General guidance, not Autodesk data: prefer feature detection (hasattr(obj, 'newProperty')) to version-string comparisons, and check minimum-version requirements at start-up. Autodesk's reference marks superseded members as RETIRED and says they continue to work for backward compatibility but are no longer officially supported; watch for that marker.
5. Version and changelog#
Increment the manifest version for every release. Keep a CHANGELOG.md. Use the version in an About command so users can report it.
6. Test on a clean profile#
Copy the folder to another machine, or to a different user account. Check:
- The add-in appears in the dialog.
- It runs without VS Code or your vendored packages missing.
- It starts with Fusion if
runOnStartupis true. - Stopping removes buttons.
- It does not throw on macOS if marked as supporting it.
- A fresh Fusion with no open document does not break
run: the reference saysactiveDocumentandactiveProductreturn null when no documents are open. UseApplication.isStartupCompleteor thestartupCompletedevent if you need to wait for startup to finish.
Distribution routes#
Route A: zip file#
- Zip the folder so the zip root contains the add-in folder itself.
- Give instructions: unzip into the AddIns folder (or the
FusionAddinsfolder, which Autodesk reserves for non-store apps); open Scripts and Add-Ins; set Run on Startup if desired; click the run icon. - Alternatively users can choose the + menu > Script or add-in from device and select the folder wherever it is; Fusion links it, and Unlink later forgets it without deleting the folder.
Pros: no infrastructure. Cons: manual updates, user error.
Route B: installer script#
Provide a script that copies files into a folder Fusion searches automatically. Autodesk documents FusionAddins as the place for apps that are not from the App Store; they "follow the standard folder structure, with each add-in in a folder with the same name as the add-in" and need no .bundle folder.
# install.py — run with a normal system Python
import os, shutil, sys
SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'MyTool')
if sys.platform == 'win32':
dest_root = os.path.join(os.environ['APPDATA'], 'Autodesk', 'FusionAddins')
else:
dest_root = os.path.expanduser('~/Library/Application Support/Autodesk/FusionAddins')
os.makedirs(dest_root, exist_ok=True)
dest = os.path.join(dest_root, 'MyTool')
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(SRC, dest)
print('Installed to', dest)
If you installed with a Windows installer, uninstall through the installer (Start menu, or Installed apps); otherwise delete the folder. For enterprise deployment, general guidance: package the same copy step in a script run by your IT tooling.
Route C: Autodesk App Store#
What Autodesk's help says: apps downloaded from the Autodesk App Store are installed to %appdata%\Autodesk\ApplicationPlugins (Mac: ~/Library/Application Support/Autodesk/ApplicationPlugins), a location shared by all Autodesk applications; each add-in sits in a .bundle folder, and a PackageContents.xml file identifies it as a Fusion app. The Scripts and Add-Ins dialog's + menu has a "go to the Autodesk App Store" item.
Note: not confirmed in Autodesk's documentation: the layout inside the
.bundlefolder, the contents ofPackageContents.xml, the store's submission and review process, payment handling, and legal terms. Consult the App Store publisher pages before building a release pipeline.
Licensing and protection#
Python source is readable. Options if you need to protect it:
- Ship compiled C++ add-ins (see the C++ page).
- Ship
.pycbytecode only; this is trivially decompiled and also version-sensitive, so it is a poor choice. - Move the sensitive logic to a web service you control and call it from the add-in with
urllib.request. This gives licence enforcement, at the cost of requiring a network. - Use a simple licence key check with an online verification endpoint.
Be honest in your documentation about what data your add-in sends off the machine. Users and their IT departments care.
Options reference#
| Item | Choice | Notes |
|---|---|---|
| Distribution | Zip, installer script, App Store | Increasing effort and reach |
| Updating | Replace the folder, bump version | General guidance: stop the add-in first |
| Settings storage | User data folder | Never inside the add-in folder |
| Dependencies | Vendored in lib/ | Pin versions; test on both OSes |
| OS support | supportedOS in manifest | `windows |
| Startup | runOnStartup | Keep startup work minimal |
| Identification | Command-definition ids; manifest id is unused | Namespace command ids with your company name |
| Licence | Include LICENSE file | Also state third-party licences |
Tips and pitfalls#
Tip: Keep start-up cost tiny. An add-in with Run on Startup delays Fusion launch. Defer expensive imports until the user first clicks your command.
Tip: Provide an uninstall note: stop the add-in, remove it from the dialog, and delete the folder.
Warning: Do not assume a document is open in
runat start-up. Testapp.activeProductandapp.activeDocumentforNone.
Warning: General guidance: do not change command-definition ids between releases without a reason; users may have customised toolbars or shortcuts around them.
Warning: Including Autodesk logos or implying Autodesk endorsement in your add-in's name and icon can violate trademark rules. Use your own branding and say "for Autodesk Fusion" if needed.
Warning: C++ add-ins are compiled separately for each platform (Autodesk's C++ topic), and
supportedOScan restrict an item to one. General guidance: bundled binary Python wheels are specific to OS, architecture and Python version, which the documentation does not state for Fusion.
Practice exercise#
Take the "Quick Box" add-in from the UI page and prepare it for release:
- Add a
settings.jsonin the user data folder remembering the last size. - Write a
CHANGELOG.mdand setversionto1.0.0in the manifest. - Leave
idempty (Autodesk says it is unused) and setautodeskProducttoFusion. - Zip it and install it on a second machine or a second OS user account using the Script or add-in from device option.
- Run, stop, and run again to prove cleanup.
Expected result: the add-in works from a fresh location, retains the last used size across Fusion restarts, and leaves no buttons after Stop.
Related#
- UI: commands, toolbars, palettes
- Debugging in VS Code
- C++ API notes
- Autodesk Platform Services