Plugin Types Guide

UniCore supports six plugin types. Each type integrates into a different part of the platform and uses a dedicated define*() helper.

Agent Plugins

Register custom AI agents into the OpenClaw multi-agent framework. Agents have a system prompt, model, and tool set. Once registered, they appear in the agent roster and can be triggered by workflows, messages, or other agents.

Industry-specific assistantsSales & support copilotsAutonomous task agentsSpecialist sub-agents
src/index.tstypescript
import { definePlugin, defineAgent } from '@bemindlabs/unicore-plugin-sdk'

const salesCopilot = defineAgent({
  id: 'sales-copilot',
  name: 'Sales Copilot',
  description: 'AI agent that qualifies leads and drafts follow-up emails.',
  model: 'gpt-4o',
  systemPrompt: `You are a sales assistant for {{company_name}}.
Your goal is to qualify inbound leads and suggest next actions.`,

  tools: [
    {
      name: 'getContactHistory',
      description: 'Fetch the CRM history for a contact',
      parameters: { type: 'object', properties: { contactId: { type: 'string' } }, required: ['contactId'] },
      async execute({ contactId }, ctx) {
        return ctx.db.contact.findUnique({ where: { id: contactId } })
      },
    },
  ],
})

export default definePlugin({
  id: 'my-sales-copilot',
  name: 'Sales Copilot',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.agents.register(salesCopilot)
  },
})

Integration Plugins

Connect external SaaS tools to UniCore using OAuth 2.0 or API key auth. Integration plugins can subscribe to external webhooks, sync data bidirectionally, and expose a Connect modal in the admin dashboard.

E-commerce (Shopify, WooCommerce)Accounting (QuickBooks, Xero)CRM (HubSpot, Salesforce)Productivity (Notion, Google Workspace)
src/index.tstypescript
import { definePlugin, defineIntegration } from '@bemindlabs/unicore-plugin-sdk'

const shopifyIntegration = defineIntegration({
  id: 'shopify',
  name: 'Shopify',
  description: 'Sync orders and customers from Shopify.',
  icon: 'https://cdn.shopify.com/icon.svg',

  // OAuth 2.0 setup shown in the admin Connect modal
  auth: {
    type: 'oauth2',
    authUrl: 'https://{{shop}}.myshopify.com/admin/oauth/authorize',
    tokenUrl: 'https://{{shop}}.myshopify.com/admin/oauth/access_token',
    scopes: ['read_orders', 'read_customers'],
  },

  // Webhooks UniCore will subscribe to on your behalf
  webhooks: [
    { event: 'orders/create', handler: 'onOrderCreated' },
    { event: 'customers/create', handler: 'onCustomerCreated' },
  ],

  handlers: {
    async onOrderCreated(payload, ctx) {
      await ctx.db.order.upsert({ where: { externalId: payload.id }, ... })
    },
    async onCustomerCreated(payload, ctx) {
      await ctx.db.contact.upsert({ where: { externalId: payload.id }, ... })
    },
  },
})

export default definePlugin({
  id: 'shopify-integration',
  name: 'Shopify Integration',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.integrations.register(shopifyIntegration)
  },
})

Channel Plugins

Add new messaging channels to UniCore's omnichannel inbox. Channel plugins implement a send() function and optionally set up inbound webhooks so incoming messages are routed to the correct conversation.

Discord botSMS providersCustom chat widgetsEnterprise messaging (Teams, Slack)
src/index.tstypescript
import { definePlugin, defineChannel } from '@bemindlabs/unicore-plugin-sdk'

const discordChannel = defineChannel({
  id: 'discord',
  name: 'Discord',
  description: 'Send and receive messages via Discord bot.',
  icon: 'https://assets-global.website-files.com/6257adef93867e50d84d30e2/icon.png',

  async send(message, ctx) {
    const { channelId } = await ctx.settings.get('discord_channel_id')
    await fetch(`https://discord.com/api/channels/${channelId}/messages`, {
      method: 'POST',
      headers: {
        Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ content: message.text }),
    })
  },

  // Register an incoming webhook to receive messages from Discord
  async setupInbound(webhookUrl, ctx) {
    ctx.log.info('Configure your Discord bot to POST to:', webhookUrl)
  },
})

export default definePlugin({
  id: 'discord-channel',
  name: 'Discord Channel',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.channels.register(discordChannel)
  },
})

Workflow Node Plugins

Add custom nodes to the visual workflow builder. Each node declares typed inputs and outputs, an icon, and an execute() function. Registered nodes appear in the node palette and can be used in any workflow.

Third-party API callsData transformation stepsConditional branching logicNotification dispatch
src/index.tstypescript
import { definePlugin, defineWorkflowNode } from '@bemindlabs/unicore-plugin-sdk'

const sendSlackNode = defineWorkflowNode({
  id: 'send-slack-message',
  name: 'Send Slack Message',
  description: 'Post a message to a Slack channel.',
  category: 'Messaging',
  icon: 'slack',

  // Inputs shown in the visual workflow builder
  inputs: [
    { id: 'channel', label: 'Channel', type: 'string', placeholder: '#general' },
    { id: 'text',    label: 'Message',  type: 'text' },
  ],

  // Output variables available to downstream nodes
  outputs: [
    { id: 'messageId', label: 'Message ID', type: 'string' },
  ],

  async execute({ channel, text }, ctx) {
    const token = await ctx.settings.get<string>('slack_bot_token')
    const response = await fetch('https://slack.com/api/chat.postMessage', {
      method: 'POST',
      headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ channel, text }),
    })
    const data = await response.json()
    return { messageId: data.ts }
  },
})

export default definePlugin({
  id: 'slack-workflow-nodes',
  name: 'Slack Workflow Nodes',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.workflow.registerNode(sendSlackNode)
  },
})

Analytics & Report Plugins

Register custom reports that appear in the Analytics section of the dashboard. Reports can use AI scoring, database queries, and external APIs. They support date-range filters and export to CSV/PDF.

AI-powered churn predictionCustom KPI dashboardsIndustry-specific reportsMulti-source data blending
src/index.tstypescript
import { definePlugin, defineReport } from '@bemindlabs/unicore-plugin-sdk'

const churnRiskReport = defineReport({
  id: 'churn-risk',
  name: 'Churn Risk Analysis',
  description: 'Identify customers at risk of churning using AI scoring.',
  category: 'Customer Intelligence',

  // Filters shown in the report header
  filters: [
    { id: 'period', label: 'Period', type: 'daterange', default: '30d' },
    { id: 'minScore', label: 'Min Risk Score', type: 'number', default: 0.7 },
  ],

  async generate({ period, minScore }, ctx) {
    const customers = await ctx.db.contact.findMany()
    // AI-powered scoring
    const scored = await Promise.all(customers.map(async (c) => {
      const score = await ctx.ai.chat({
        model: 'gpt-4o-mini',
        messages: [{ role: 'user', content: `Score churn risk 0-1 for: ${JSON.stringify(c)}` }],
      })
      return { ...c, churnScore: parseFloat(score.content) }
    }))
    return scored.filter((c) => c.churnScore >= minScore)
  },
})

export default definePlugin({
  id: 'churn-risk-analysis',
  name: 'Churn Risk Analysis',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.reports.register(churnRiskReport)
  },
})

Theme Plugins

Package a complete UI theme as a plugin. Theme plugins define design tokens (colors, typography, spacing, radius) that override the default Tailwind CSS variables. Themes can be switched per-tenant in the branding settings.

White-label deploymentsClient-specific brandingSeasonal themesAccessibility-focused palettes
src/index.tstypescript
import { definePlugin, defineTheme } from '@bemindlabs/unicore-plugin-sdk'

const midnightTheme = defineTheme({
  id: 'midnight-pro',
  name: 'Midnight Pro',
  description: 'A deep dark theme with cyan accents.',
  preview: 'https://example.com/midnight-preview.png',

  tokens: {
    '--color-bg-primary':    '#0a0a0f',
    '--color-bg-secondary':  '#12121a',
    '--color-bg-tertiary':   '#1a1a26',
    '--color-text-primary':  '#e2e8f0',
    '--color-text-secondary':'#94a3b8',
    '--color-accent':        '#06b6d4',   // cyan-500
    '--color-accent-hover':  '#0891b2',   // cyan-600
    '--color-border':        '#1e293b',
    '--radius-base':         '0.5rem',
    '--font-sans':           '"JetBrains Mono", monospace',
  },
})

export default definePlugin({
  id: 'midnight-pro-theme',
  name: 'Midnight Pro Theme',
  version: '1.0.0',

  async onActivate(ctx) {
    ctx.themes.register(midnightTheme)
  },
})

Combining multiple types

A single plugin can register multiple types. For example, a Shopify plugin might register an integration (for OAuth + webhook sync), a workflow node (Create Shopify Order), and an analytics report (Sales by Channel). Simply call multiple ctx.*register() methods inside onActivate().