Developing plugins
Create custom plugins to extend VerifyWise with new features.
Overview
Create custom plugins to extend VerifyWise with new features tailored to your organization. This guide covers the plugin architecture, available APIs, and best practices for plugin development.
Getting started
The fastest way to create a plugin is to start from a template:
- Clone the plugin marketplace repository from GitHub
- Copy a template that matches your use case
- Modify the manifest.json with your plugin details
- Implement your plugin logic in index.ts or index.js
- Test by copying to your VerifyWise plugins directory
# Clone the marketplace repository
git clone https://github.com/bluewave-labs/plugin-marketplace.git
# Copy a template
cp -r templates/template-basic-plugin plugins/my-plugin
# Test locally
cp -r plugins/my-plugin /path/to/verifywise/Servers/plugins/marketplace/Plugin structure
Every plugin requires the following files:
my-plugin/
āāā manifest.json # Plugin metadata and configuration schema
āāā index.ts # Main plugin code (or index.js)
āāā icon.svg # Optional plugin icon (24x24 recommended)The manifest file
The manifest.json file defines your plugin metadata, permissions, and configuration options:
{
"id": "my-plugin",
"name": "My Plugin",
"description": "A brief description of what my plugin does",
"version": "1.0.0",
"author": {
"name": "Your Name",
"url": "https://your-website.com"
},
"type": "feature",
"tags": ["custom", "example"],
"permissions": [
"events:listen",
"database:read"
],
"config": {
"apiKey": {
"type": "string",
"required": true,
"secret": true,
"label": "API key",
"description": "Your service API key"
}
},
"compatibility": {
"minVersion": "1.6.0"
}
}Manifest fields
| Field | Description |
|---|---|
| id | Unique identifier using lowercase letters, numbers, and hyphens |
| name | Display name shown in the UI |
| description | Brief summary of plugin functionality |
| version | Semantic version (major.minor.patch) |
| type | Category: integration, feature, framework, or reporting |
| permissions | Array of required permissions |
| config | Configuration schema for plugin settings |
Plugin code
Your plugin exports a default object that implements the Plugin interface:
import type { Plugin, PluginContext } from "../core";
const myPlugin: Plugin = {
id: "my-plugin",
name: "My Plugin",
// Called when plugin is loaded
async onLoad(context: PluginContext) {
console.log("Plugin loaded!");
},
// Called when plugin is unloaded
async onUnload(context: PluginContext) {
console.log("Plugin unloaded!");
},
// Called when plugin is enabled
async onEnable(context: PluginContext) {
console.log("Plugin enabled!");
},
// Called when plugin is disabled
async onDisable(context: PluginContext) {
console.log("Plugin disabled!");
},
};
export default myPlugin;Plugin context
The PluginContext object provides access to VerifyWise APIs:
- context.config: Read plugin configuration values
- context.events: Subscribe to and emit events
- context.logger: Log messages with proper formatting
- context.storage: Store plugin-specific data
- context.models: Access Sequelize models (with permission)
- context.scheduler: Schedule recurring tasks
- context.metadata: Attach custom data to entities
Event system
Plugins can listen to system events and emit their own:
async onEnable(context: PluginContext) {
// Listen for model updates
context.events.on("model:updated", async (payload) => {
context.logger.info("Model updated:", payload.modelId);
});
// Listen for task completions
context.events.on("task:completed", async (payload) => {
// Send notification to external service
await notifyExternalService(payload);
});
}Common events you can listen to:
- model:created, model:updated, model:deleted: AI model changes
- task:created, task:completed: Task lifecycle events
- risk:identified, risk:mitigated: Risk management events
- compliance:status-changed: Compliance status updates
- user:login, user:logout: Authentication events
Dashboard widgets
Plugins can provide dashboard widgets that users can add to their view:
const myPlugin: Plugin = {
id: "my-plugin",
name: "My Plugin",
widgets: [
{
id: "my-widget",
name: "My Custom Widget",
description: "Displays custom metrics",
template: "stats-card",
defaultSize: { w: 2, h: 1 },
dataEndpoint: "/api/plugins/my-plugin/widget-data",
}
],
};Available widget templates:
| Template | Best for |
|---|---|
| stats-card | Displaying key metrics and statistics |
| list | Showing lists of items or activities |
| table | Tabular data with columns |
| chart | Visualizing trends and data over time |
| progress | Progress bars and completion status |
| timeline | Chronological events and activities |
| calendar | Date-based events and schedules |
Custom pages
Add custom pages to the sidebar navigation:
const myPlugin: Plugin = {
id: "my-plugin",
name: "My Plugin",
pages: [
{
id: "my-page",
title: "My Custom Page",
icon: "Settings",
path: "/plugins/my-plugin",
// Use iframe to embed external content
iframe: {
src: "https://my-service.com/embed",
sandbox: ["allow-scripts", "allow-same-origin"],
},
// Or provide HTML content directly
// content: "<div>My page content</div>",
}
],
};Custom API routes
Plugins can register custom API endpoints:
const myPlugin: Plugin = {
id: "my-plugin",
name: "My Plugin",
routes: [
{
method: "GET",
path: "/status",
handler: async (req, res, context) => {
res.json({ status: "ok", timestamp: Date.now() });
},
},
{
method: "POST",
path: "/webhook",
handler: async (req, res, context) => {
// Process incoming webhook
const data = req.body;
context.events.emit("my-plugin:webhook-received", data);
res.json({ received: true });
},
},
],
};Routes are automatically prefixed with /api/plugins/{plugin-id}/.
Scheduled tasks
Use the scheduler API to run periodic tasks:
async onEnable(context: PluginContext) {
// Run every hour
context.scheduler.register({
id: "hourly-sync",
cron: "0 * * * *",
handler: async () => {
context.logger.info("Running hourly sync...");
await syncData();
},
});
}
async onDisable(context: PluginContext) {
// Clean up scheduled tasks
context.scheduler.unregister("hourly-sync");
}Configuration options
Define configuration fields in your manifest for user-configurable settings:
"config": {
"apiKey": {
"type": "string",
"required": true,
"secret": true,
"label": "API key",
"description": "Your service API key"
},
"syncInterval": {
"type": "number",
"default": 60,
"label": "Sync interval (minutes)",
"description": "How often to sync data"
},
"enableNotifications": {
"type": "boolean",
"default": true,
"label": "Enable notifications",
"description": "Send notifications for important events"
}
}Access configuration values in your plugin code:
async onEnable(context: PluginContext) {
const apiKey = context.config.get("apiKey");
const interval = context.config.get("syncInterval") || 60;
if (!apiKey) {
throw new Error("API key is required");
}
}Testing your plugin
To test your plugin during development:
- Copy your plugin to the plugins/marketplace directory
- Restart the VerifyWise server
- Navigate to Settings > Plugins
- Find and enable your plugin
- Check server logs for errors
- Test all plugin features
Publishing to the marketplace
To share your plugin with the community:
- Fork the plugin-marketplace repository on GitHub
- Add your plugin to the plugins/ directory
- Create a zip file of your plugin
- Add an entry to registry.json
- Submit a pull request
Best practices
- Request minimal permissions: Only request permissions your plugin actually needs
- Handle errors gracefully: Use try-catch and provide meaningful error messages
- Clean up on disable: Remove event listeners and scheduled tasks when disabled
- Use the logger: Log important events for debugging and auditing
- Validate configuration: Check required settings before enabling
- Document your plugin: Include clear descriptions and usage instructions
- Version semantically: Use semantic versioning for releases