Coming in Broadcast 3.0. Until then, this page applies only to the Upcoming (edge) build.

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 zip command. 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:

  1. export default definePlugin({ ... }) is how every plugin starts. definePlugin is ready for you. Do not import it.
  2. onEvent is the hook. Broadcast calls it with two values: event says what happened, and ctx is your way to talk to Broadcast.
  3. ctx.settings.get("tag_name") reads the setting.
  4. event.data.subscriber_id is the ID of the new person.
  5. ctx.broadcast.tags.add(...) asks Broadcast to add the tag. The await matters: without it, your function can end before the work is done.
  6. 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

  1. Go to Application > Plugins.
  2. In Upload a plugin, choose your file.
  3. 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, and write includes read.
  • 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

  1. On the plugin page, find Channels.
  2. Click Open next to the channel you want to use.
  3. Find the Signup tagger card and click Settings and permissions.
  4. In Tag for new subscribers, type new-subscriber.
  5. 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

  1. Add a subscriber to that channel. Use a test address that you own.
  2. Wait a few seconds, then open that subscriber.

You should see the tag new-subscriber.

Now read the log:

  1. Go to Application > Plugins and click Signup tagger.
  2. 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:

  1. Is the plugin on in this channel? The card must show On. Each channel has its own switch.
  2. Did you add the person to the same channel?
  3. Is the person new? The event subscriber.created runs once, when a subscriber is first created. Changing an existing subscriber does not run it.
  4. 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 for tags: write.
  5. 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.
  6. 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.
  • ctx is your one way to talk to Broadcast.

Next steps

Last updated

Was this page helpful?

Thanks for your feedback!

Thanks for letting us know. We'll work on improving this page.