Your First Plugin
In this tutorial you build a plugin named Signup tagger. When a new person subscribes, the plugin adds a tag to that person. The admin chooses the tag.
It is a small plugin, but it uses the parts that every plugin uses: a manifest, a hook, a permission, a setting and a log line.
What you need
- A Broadcast server, version 3.0 or later, and a user with sudo access. If you are not an admin, ask one to do steps 6 to 8.
- A text editor.
- A terminal with the
zipcommand. macOS and Linux have it. On Windows, use WSL or Git Bash.
You do not need Node, npm or any install. A plugin is a few text files in a zip file.
Before you start, sign in to Broadcast and go to Application > Plugins. You should see The plugin runtime is running. If you see The plugin runtime is not running or not installed, plugins cannot run on this server yet. Everything else in Broadcast still works.
Step 1: Make the folders
Run these commands:
mkdir -p signup-tagger/dist cd signup-tagger
You now have an empty folder signup-tagger, with one folder dist in it. The name dist is fixed, because Broadcast looks there for your code.
Step 2: Write the manifest
The manifest tells Broadcast what your plugin is. Make a file named manifest.json in signup-tagger, with this content:
{ "id": "local.signup-tagger", "name": "Signup tagger", "version": "1.0.0", "requires": { "broadcast": ">=3.0.0 <4.0.0", "pluginApi": "1" }, "permissions": { "tags": "write" }, "hooks": { "events": ["subscriber.created"] }, "settings": { "tag_name": { "type": "string", "label": "Tag for new subscribers", "required": true } } }
Here is what each part means:
| Part | Meaning |
|---|---|
id |
The name Broadcast uses inside. Your own plugins must start with local. |
name |
The name people see |
version |
The version of your plugin. Change it each time you release |
requires |
The versions of Broadcast and of the plugin API that this plugin works with. Copy this line as it is |
permissions |
What the plugin may do. Here: add and remove tags |
hooks |
When Broadcast calls your code. Here: when a subscriber is created |
settings |
Values the admin fills in. Here: the name of the tag |
Do not add comments to this file. JSON does not allow them.
Step 3: Write the code
Make a file named main.js in the dist folder, with this content:
export default definePlugin({ async onEvent(event, ctx) { const tag = ctx.settings.get("tag_name"); const subscriberId = event.data.subscriber_id; await ctx.broadcast.tags.add({ subscriber_id: subscriberId, tags: [tag] }); ctx.log.info(`Added the tag ${tag} to subscriber ${subscriberId}.`); }, });
Read it line by line:
export default definePlugin({ ... })is how every plugin starts.definePluginis ready for you. Do not import it.onEventis the hook. Broadcast calls it with two values:eventsays what happened, andctxis your way to talk to Broadcast.ctx.settings.get("tag_name")reads the setting.event.data.subscriber_idis the ID of the new person.ctx.broadcast.tags.add(...)asks Broadcast to add the tag. Theawaitmatters: without it, your function can end before the work is done.ctx.log.info(...)writes a line that the admin can read.
Your folder now has two files:
signup-tagger/
manifest.json
dist/
main.js
Step 4: Pack the plugin
A .broadcast-plugin file is a plain zip file with a different name. Run this command in the signup-tagger folder:
zip -r -X ../signup-tagger-1.0.0.broadcast-plugin manifest.json dist
You should see this:
adding: manifest.json (deflated 44%) adding: dist/ (stored 0%) adding: dist/main.js (deflated 36%)
The numbers can be different. You now have the file signup-tagger-1.0.0.broadcast-plugin next to your folder.
Warning
Run zip from inside the plugin folder, and do not use the Compress command in Finder. manifest.json must be at the top of the zip file, and Finder adds hidden files that Broadcast refuses.
Step 5: Upload it
- Go to Application > Plugins.
- In Upload a plugin, choose your file.
- Click Upload.
You should see the page Signup tagger 1.0.0 and the message Uploaded. Check the permissions, then deploy.
If you see a red message on the plugins page, your package has a problem, and nothing was installed. Here are the ones you are most likely to see:
| Message | What to do |
|---|---|
The package has no manifest.json. or signup-tagger/manifest.json is not in an allowed folder |
You zipped the folder from the outside. Run zip from inside the folder, as in step 4 |
manifest.json is not valid JSON: ... |
Look for a missing comma, an extra comma after the last item, or a comment |
dist/main.js: dist/main.js does not parse: ... |
Your code has a syntax error. The message gives the line |
"__MACOSX/._manifest.json" is not an allowed file name. |
You used Finder. Use the zip command |
Testing and Debugging Plugins has the full list. Fix the problem, run zip again and upload again. First delete the old zip file, or zip adds to it.
Step 6: Review and deploy
This is the page an admin sees for each new version. Read it from the top:
- A note says Not from the plugin directory. Install it only if you trust where it came from. This is normal for your own plugin.
- Permissions shows Read tags and Change tags, both marked Standard. You asked for
write, andwriteincludesread. - What it adds shows
events: subscriber.created. - Code shows your files, so that the admin can read them.
Click Deploy. You should see Version 1.0.0 is live.
Deployed does not mean running. The plugin is live on the server, and it is still off in every channel.
Step 7: Fill in the setting
- On the plugin page, find Channels.
- Click Open next to the channel you want to use.
- Find the Signup tagger card and click Settings and permissions.
- In Tag for new subscribers, type
new-subscriber. - Click Save settings.
You should see Settings saved.
If you turn the plugin on first, you see Fill in these settings first: tag_name. The setting is required in your manifest, so Broadcast asks for it.
Step 8: Turn it on
Click the switch on the Signup tagger card. You should see The plugin is on in and the name of your channel, and the card shows On.
Step 9: See it work
- Add a subscriber to that channel. Use a test address that you own.
- Wait a few seconds, then open that subscriber.
You should see the tag new-subscriber.
Now read the log:
- Go to Application > Plugins and click Signup tagger.
- Click Logs.
You should see a line like this:
info onEvent Added the tag new-subscriber to subscriber 12.
That is your plugin at work. You built it, packed it, reviewed it and ran it.
If nothing happens
Check these in order:
- Is the plugin on in this channel? The card must show On. Each channel has its own switch.
- Did you add the person to the same channel?
- Is the person new? The event
subscriber.createdruns once, when a subscriber is first created. Changing an existing subscriber does not run it. - Is there a red line in the logs? The message tells you what went wrong. For example,
The plugin may not use tags.add.means the manifest does not ask fortags: write. - Did you wait? Events run as background jobs. Also, after you turn a plugin on, Broadcast can take up to 30 seconds to start sending it events.
- Does the plugin page say it was turned off? A plugin that fails 20 times in a row is turned off for all channels. Fix the bug and deploy a new version.
Step 10: Release a change
Now change the plugin. Change the log line in dist/main.js:
ctx.log.info(`Tagged subscriber ${subscriberId} in the channel ${ctx.channel.name}.`);
A version number is a promise that the code does not change. So you must also change the version in manifest.json:
"version": "1.0.1",
Then pack it with the new name:
zip -r -X ../signup-tagger-1.0.1.broadcast-plugin manifest.json dist
Upload the file. You see the review page again, this time with Changes since 1.0.0. It shows only what changed. Click Deploy.
If you forget to change the version, you see Version 1.0.0 is already installed with other code. A published version never changes: use a new version number.
The channel stays on and the setting stays saved. The next new subscriber runs your new code.
Step 11: Add a page
A plugin can have its own page in Broadcast. Signal, the UI kit, makes it easy. Add a page that shows how many people the plugin tagged.
Make a folder for it, in the signup-tagger folder:
mkdir ui
Change manifest.json to this. The new parts are the version and ui:
{ "id": "local.signup-tagger", "name": "Signup tagger", "version": "1.1.0", "requires": { "broadcast": ">=3.0.0 <4.0.0", "pluginApi": "1" }, "permissions": { "tags": "write" }, "hooks": { "events": ["subscriber.created"] }, "settings": { "tag_name": { "type": "string", "label": "Tag for new subscribers", "required": true } }, "ui": { "pages": [ { "slot": "nav", "title": "Signup tagger", "path": "ui/index.html" } ] } }
Change dist/main.js to this. It now counts each tag, and pageData gives the numbers to the page:
export default definePlugin({ async onEvent(event, ctx) { const tag = ctx.settings.get("tag_name"); const subscriberId = event.data.subscriber_id; await ctx.broadcast.tags.add({ subscriber_id: subscriberId, tags: [tag] }); await ctx.store.increment("tagged"); ctx.log.info(`Added the tag ${tag} to subscriber ${subscriberId}.`); }, async pageData(request, ctx) { return { tag: ctx.settings.get("tag_name"), tagged: (await ctx.store.get("tagged")) ?? 0, }; }, });
Make ui/index.html. The two lines in <head> load the kit from your server:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Signup tagger</title> <link rel="stylesheet" href="/plugin-kit/v1/kit.css"> <script type="module" src="/plugin-kit/v1/kit.js"></script> <script type="module" src="app.js"></script> </head> <body> <sg-page title="Signup tagger" description="Tags each new subscriber in this channel."> <sg-card id="summary"><sg-skeleton lines="2"></sg-skeleton></sg-card> </sg-page> </body> </html>
Make ui/app.js:
const summary = document.getElementById("summary"); function stat(label, value) { const node = document.createElement("sg-stat"); node.setAttribute("label", label); node.setAttribute("value", value); return node; } try { const data = await sg.pageData(); const grid = document.createElement("sg-grid"); grid.append(stat("Tag", data.tag), stat("Subscribers tagged", data.tagged)); summary.replaceChildren(grid); } catch (error) { sg.toast(`The page could not load: ${error.message}`, { tone: "danger" }); }
Pack it, now with ui:
zip -r -X ../signup-tagger-1.1.0.broadcast-plugin manifest.json dist ui
Upload it and click Deploy. Then open Plugins in the sidebar and click Signup tagger. You should see the tag, and the number of subscribers tagged since this version.
What you learned
- A plugin is a manifest and one JavaScript file in a zip file.
- The manifest declares everything. Code cannot use what the manifest does not ask for.
- An admin reviews and deploys each version, then turns the plugin on per channel.
ctxis your one way to talk to Broadcast.
Next steps
- Signal Quick Start builds a richer page.
- Plugin Core Concepts explains each idea you used here.
- Testing and Debugging Plugins shows how to test code before you deploy it.
- Plugin Hooks shows what else your plugin can do.