1. Get the starter
The fastest path is to clone the extension starter, a Vite project wired for the whole build:2. Write the module
A driver exportsactivate and onEvent. Here is a complete extension that adds a context-menu item, and each time it is clicked drops a sticky note on the canvas and flies the camera to it:
issueCommandis how you talk to Drawdy. Every call takes{ type, driverId, requestId, req }and resolves to a response.driverIdcomes from your manifest;requestIdis any value unique within your driver.- Subscriptions are set up by issuing a command.
subscription:context-menu:clickedregisters interest; the matching events then arrive inonEvent. generateIdmints ids for the canvas elements you create.
3. The manifest
Every extension declares amanifest.json:
main is the entry bundle inside the .drawdyx zip. driverVersion is a semver version; every update you publish needs a higher one. driverId is the stable identity Drawdy uses for install, storage, and command routing. permissions declares what the driver may do; the starter adds a context menu (dom) and draws on the canvas (scene). Drawdy asks the user to grant each one the first time a command needs it. See Permissions.
4. Run it in development
http://localhost:5173 by default — it prints the URL). On every save it rebuilds your extension, serves it at /built.drawdyx, and bumps a /version endpoint. Drawdy watches that endpoint and hot-reloads your driver on each build — no reinstall.
Point Drawdy at your dev server
Open a local board
Add the dev server
⌘K (macOS) or Ctrl+K (Windows/Linux) and run Add extension dev server. Enter your dev server URL, e.g. http://localhost:5173 (a bare localhost:5173 works too).Edit and save
<your-url>/version every second, and when it changes it fetches <your-url>/built.drawdyx and reloads the driver in place.ws:// and wss:// URLs are accepted and normalized to http/https; the path is ignored, so only the host matters.
Develop several extensions at once
Nothing stops you running more than one at a time. Each dev link is an independent connection: Drawdy polls each one separately and installs the driver it serves under that driver’s owndriverId, so all of them live on the same board side by side.
To add a second extension:
Run its dev server on a different port
5173 with strictPort, so a second npm run dev would fail on the same port. Start it on another port instead:server.port in that extension’s vite.config.ts).Add a second dev server
http://localhost:5174. The palette now lists a Remove extension dev server entry per link, so you can drop them one at a time.driverId in its manifest.json. Two dev
servers serving the same driverId install over each other — Drawdy routes
by driverId, so the id is what keeps them separate.5. Package for distribution
dist/<driver-id>.drawdyx — the zip of your manifest.json and main.js. That single file is the whole extension. Anyone can install it into Drawdy.
The .drawdyx format
A .drawdyx is a plain zip with two entries:
activate and onEvent exports. The starter’s Rollup config emits exactly this; if you roll your own build, target format: "cjs" with exports: "named".
6. Prepare to submit
You submit from a GitHub repository, not from the.drawdyx you built. Drawdy fetches your source from GitHub and builds it with its own build rules. Choose how you want to share it:
Repository rules
These rules apply to public and private extensions alike. The repository must be public on GitHub.-
manifest.jsonat the repository root. It must be valid JSON with these fields:Drawdy won’t install an extension whose manifest has nodescriptionorpermissions. Use an empty array if the driver needs no permission. See Permissions. -
An entry file at
src/index.ts,src/index.tsxorsrc/index.js. Drawdy bundles it with esbuild into one CommonJS file targeting ES2020 in the browser. -
No npm dependencies. Relative imports work, but the only package you may import is
@drawdy/driver-protocol, which is types only. Drawdy does not runnpm install, so any other package import fails the build. Copy the code you need into your repository instead. -
No bundler plugins. Your Vite or Rollup config is not used. If the starter generates a module at build time (a
virtual:import, such as webview HTML), commit the generated file as source, e.g.src/webview-html.ts, and import that. -
Size limits. At most 500 files and 20 MB across the whole repository, not only
src/. The built bundle must stay under 5 MB.
- Icon: the
iconpath from the manifest, or elseicon.png,icon.webporicon.svgat the root. PNG, WebP or SVG, up to 256 KB. SVGs containing scripts are rejected. README.mdat the root, up to 200 KB. It becomes the extension’s overview page. Relative links and images are rewritten to point at your repository on GitHub.CHANGELOG.mdat the root, up to 200 KB. It appears in the Changelog tab.
Get an API token
Skip this if you use the page on drawdy.io. To submit with the API, from a script or an AI agent, you need a Drawdy token. You approve it once in the browser, so the agent never needs your password. Request a device code:verification_uri_complete while signed in to Drawdy and click Approve. Meanwhile, poll for the token every interval seconds:
authorization_pending. Keep polling, and add 5 seconds to the interval on slow_down. After approval it returns { "access_token": "…", "token_type": "Bearer", "expires_in": 2592000 }. The token lasts 30 days. Send it on every API call in sections 7 and 8 as Authorization: Bearer <access_token>.
The API is at https://api.drawdy.io. Every failed call returns { "error": "<message>" }, and the message says what to fix.
7. Submit a public extension
A public extension is listed in the Marketplace, where anyone can install it. The Drawdy team reviews every version before it goes live. Your GitHub account must have push access to the repository. You can submit up to 10 times per day.With the form
Push your code
Open the submit form

The Submit tab, with Public selected and GitHub not connected yet.
Connect GitHub
Enter the repository
https://github.com/you/my-extension. Optionally enter a branch, tag or commit; leave it empty to submit the latest commit on the default branch.Submit for review

GitHub connected, repository and branch filled in, ready to submit.
With the API
You need a Drawdy token and a GitHub token.Get a GitHub token
gh auth token, or a fine-grained personal access token limited to that repository. Drawdy uses it only to check that your GitHub account has push access to the repository, and does not store it.Submit
ref is a branch, tag or commit SHA. HEAD means the latest commit on the default branch. A 201 returns the submission:Follow the review
{ "submissions": [...] }, newest first. Each one has a status (see the review process), plus buildError when the build failed and reviewNote once a reviewer has decided. GET /api/extensions/submissions lists your latest submissions across all extensions.The review process
Every public submission goes through the same steps. You follow them in Account → Extensions → My extensions, where each extension expands into its submission history.
My extensions: Sticky Timer expanded, with v1.2.1 in review, v1.2.0 superseded and two published versions. Wireframe Kit was delisted by Drawdy.
Building
Scan
In review
- the built bundle, line by line where the scan flagged something
- the manifest, and whether each permission matches what the extension does
- the listing: name, description, icon, README and changelog
- the repository and commit it was built from
Decision
- Approved → Published. Drawdy publishes the build straight to the Marketplace. The listing shows Live · v1.0.0, and that version is pinned to the reviewed commit.
- Rejected. The Reviewer note says what to change. Fix it and submit again; a rejected version has not been published, so you can resubmit with the same
driverVersion.
Publish an update
BumpdriverVersion in manifest.json above the published version, add an entry to CHANGELOG.md, push, and submit again. Every update goes through the full review. Until the new version is approved, the Marketplace keeps serving the current one.
After publishing
- Delist an extension from My extensions to remove it from the Marketplace, so nobody new can install it. List again puts it back.
- Users can report an extension from its Marketplace page. Drawdy reviews reports and can delist an extension. An extension delisted by Drawdy shows Delisted by Drawdy and can only be listed again by Drawdy; contact support if you think it was a mistake.
- Drawdy can mark a publisher as verified. Verified publishers get a badge next to their name on every listing.
8. Add a private extension
A private extension skips the Marketplace and the review. Use it for extensions you only want on your own boards, or to try one out before submitting it publicly. No GitHub sign-in is needed, and Drawdy stores only the repository URL.With the form
Push your code
Add it to your account
manifest.json is at the root with a description and permissions, and that an entry file exists. It stores only the repository URL.
Private needs no GitHub sign-in, only the repository URL.
Install it on a board
With the API
You need a Drawdy token. No GitHub token is needed."ref" to choose a branch, tag or commit (see which code is installed). Drawdy checks that manifest.json is at the root with a description and permissions, and that an entry file exists. A 201 returns { "extension": {...} } with its id, driverId, name and version. Then install it on a board from Extensions → Private.
GET /api/extensions/private lists the private extensions on your account, and DELETE /api/extensions/private/<id> removes one.
Which code is installed
The optional Branch, tag or commit (ref) decides which code is installed:
- Empty or a branch: installs the latest commit on that branch. Installed copies check for new commits at most every 6 hours and update to them, without a review.
- A tag or commit SHA: stays on that code until you change it.
Things to know
- Sharing. When you install a private extension on a cloud board you can edit, editors of that board get it too. Drawdy tells them it is a private extension from you and that it has not been reviewed. Viewers don’t get it.
- Removing it. Removing it from your account stops new installs. Boards where it’s already installed keep their copy until it’s uninstalled there.
- Limits. Up to 50 private extensions per account.
- GitHub rate limits. Each install or update check calls the GitHub API without signing in, which GitHub limits to 60 requests per hour per network. If you reach it, Drawdy waits and tries again later.
Learn from the examples
The protocol repo ships three runnable drivers,drawdy-hello, drawdy-math-symbols, and drawdy-physics-engine, each built with this same template. See Examples for what each one shows, or browse examples/ on GitHub.