Fusion Guide
advanced9 min read

Packaging and Distributing Add-Ins

Structure an add-in project, write the manifest, handle resources, versions and compatibility, and distribute by zip, installer script or the Autodesk App Store.

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 folder Autodesk Fusion 360; use Open File Location to confirm); %appdata%\Autodesk\ApplicationPlugins for App Store apps; and %appdata%\Autodesk\FusionAddins for 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:

  1. The folder name, the main .py file name, and the .manifest file name match; the manifest does not store the name. To rename an item, rename the folder and files.
  2. The main file defines run(context) and stop(context).
  3. 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).
  4. 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:

KeyMeaningNotes
autodeskProductFusion in the current topic (Fusion360 in the older one)Older manifests carry Fusion360
typeaddinscript for scripts
idNot used by Fusion; may be left emptyAn older version of the topic called it a GUID reserved for future use
authorDisplay nameShown in the dialog
descriptionLocale dictionary keyed by Microsoft language codeEmpty key is the default
versionAny string such as 1.0.0, 2016, R1Shown in the dialog's version column
runOnStartupWhether Fusion starts it automaticallyDefault false; the dialog's column also controls it
supportedOSwindows, mac, or `windowsmac`
editEnabledAppears in Autodesk's exampleNot described: not confirmed
iconFilenameAppears in Autodesk's example (AddInIcon.svg)Not described: not confirmed
sourcewindows, sourcemacC++ 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 runOnStartup is 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 says activeDocument and activeProduct return null when no documents are open. Use Application.isStartupComplete or the startupCompleted event if you need to wait for startup to finish.

Distribution routes#

Route A: zip file#

  1. Zip the folder so the zip root contains the add-in folder itself.
  2. Give instructions: unzip into the AddIns folder (or the FusionAddins folder, which Autodesk reserves for non-store apps); open Scripts and Add-Ins; set Run on Startup if desired; click the run icon.
  3. 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 .bundle folder, the contents of PackageContents.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 .pyc bytecode 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#

ItemChoiceNotes
DistributionZip, installer script, App StoreIncreasing effort and reach
UpdatingReplace the folder, bump versionGeneral guidance: stop the add-in first
Settings storageUser data folderNever inside the add-in folder
DependenciesVendored in lib/Pin versions; test on both OSes
OS supportsupportedOS in manifest`windows
StartuprunOnStartupKeep startup work minimal
IdentificationCommand-definition ids; manifest id is unusedNamespace command ids with your company name
LicenceInclude LICENSE fileAlso 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 run at start-up. Test app.activeProduct and app.activeDocument for None.

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 supportedOS can 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:

  1. Add a settings.json in the user data folder remembering the last size.
  2. Write a CHANGELOG.md and set version to 1.0.0 in the manifest.
  3. Leave id empty (Autodesk says it is unused) and set autodeskProduct to Fusion.
  4. Zip it and install it on a second machine or a second OS user account using the Script or add-in from device option.
  5. 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.

  • UI: commands, toolbars, palettes
  • Debugging in VS Code
  • C++ API notes
  • Autodesk Platform Services

Sources

Checked against these Autodesk help topics (opens autodesk.com):

Related pages