Skip to main content

Plugin Development Guide

Planned Feature

The WASM Plugin SDK is a planned feature and is not yet available. This guide documents the intended architecture and API for future implementation. Check the GitHub project board for current status.

Build custom plugins to extend Olympus platform functionality using WebAssembly (WASM) and our Plugin SDK.

Overview

Plugins allow you to:

  • Add custom business logic to orders, payments, and workflows
  • Integrate with external systems and APIs
  • Create custom reports and analytics
  • Extend the UI with new screens and components
  • Automate workflows based on triggers
┌─────────────────────────────────────────────────────────────────┐
│ PLUGIN ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Your Plugin (WASM) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Handlers │ │ State │ │ External │ │ │
│ │ │ (Hooks) │ │ Storage │ │ APIs │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────┴─────────┐ │
│ │ Plugin Runtime │ │
│ │ (Sandboxed) │ │
│ └─────────┬─────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Olympus Platform │ │
│ │ Orders │ Payments │ Inventory │ Analytics │ Users │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Getting Started

Prerequisites

  • Node.js 18+ or Rust toolchain
  • Olympus Developer Account (developers.olympuscloud.ai)
  • Plugin SDK CLI (npm install -g @olympus/plugin-cli)

Create a New Plugin

# Install the CLI
npm install -g @olympus/plugin-cli

# Create a new plugin project
olympus-plugin create my-plugin

# Choose your template
? Select plugin type:
> Order Modifier (Modify order calculations)
Webhook Handler (Handle external events)
Report Generator (Custom analytics)
UI Extension (Add screens/widgets)
Integration (Connect external service)

# Navigate to project
cd my-plugin

# Install dependencies
npm install

# Start development server
npm run dev

Project Structure

my-plugin/
├── olympus-plugin.json # Plugin manifest
├── src/
│ ├── index.ts # Entry point
│ ├── handlers/ # Hook handlers
│ │ ├── order.ts
│ │ └── payment.ts
│ ├── ui/ # UI components (optional)
│ │ └── settings.tsx
│ └── lib/ # Utility functions
│ └── api.ts
├── tests/
│ └── handlers.test.ts
├── package.json
└── tsconfig.json

Plugin Manifest

The olympus-plugin.json defines your plugin's configuration:

{
"name": "my-custom-plugin",
"version": "1.0.0",
"displayName": "My Custom Plugin",
"description": "Adds custom functionality to orders",
"author": {
"name": "Your Company",
"email": "dev@yourcompany.com",
"website": "https://yourcompany.com"
},
"icon": "./assets/icon.png",
"category": "operations",
"permissions": [
"orders:read",
"orders:write",
"payments:read",
"storage:read",
"storage:write"
],
"hooks": [
"order.beforeCreate",
"order.afterCreate",
"payment.beforeProcess"
],
"entrypoints": {
"main": "./dist/index.wasm",
"settings": "./dist/settings.js"
},
"settings": {
"schema": "./settings-schema.json"
},
"pricing": {
"type": "subscription",
"price": 29.99,
"currency": "USD",
"interval": "monthly",
"trial": 14
},
"requirements": {
"minPlatformVersion": "2.0.0",
"plans": ["professional", "enterprise"]
}
}

Manifest Fields

FieldRequiredDescription
nameYesUnique identifier (lowercase, hyphens)
versionYesSemantic version (1.0.0)
displayNameYesHuman-readable name
descriptionYesBrief description (max 200 chars)
authorYesDeveloper/company info
permissionsYesRequired data access
hooksNoPlatform events to handle
entrypointsYesWASM and JS entry files
pricingNoFree if not specified

Permissions

Plugins must declare required permissions:

Data Permissions

PermissionAccess
orders:readRead order data
orders:writeCreate/modify orders
payments:readRead payment info
payments:writeProcess payments
inventory:readRead inventory levels
inventory:writeModify inventory
customers:readRead customer data
customers:writeModify customer data
employees:readRead employee data
analytics:readAccess analytics data

System Permissions

PermissionAccess
storage:readRead plugin storage
storage:writeWrite plugin storage
webhooks:registerRegister webhook endpoints
notifications:sendSend push notifications
http:externalMake external HTTP requests
ui:injectInject UI components

Permission Best Practices

warning

Plugins that request more permissions than they need will face longer review times and lower installation rates. Users can see exactly which permissions your plugin requests, so only declare what you actually use.

  1. Request minimum permissions - Only what you need
  2. Explain why - Users see permission requests
  3. Handle denial gracefully - Some permissions may be denied
  4. Audit regularly - Remove unused permissions

Hooks & Extension Points

Available Hooks

Order Hooks

// Before order is created
export async function onOrderBeforeCreate(
context: HookContext,
order: OrderDraft
): Promise<OrderDraft | HookError> {
// Validate or modify order before creation
if (order.items.length === 0) {
return { error: 'ORDER_EMPTY', message: 'Order must have items' };
}

// Add custom field
order.metadata.customField = 'value';

return order;
}

// After order is created
export async function onOrderAfterCreate(
context: HookContext,
order: Order
): Promise<void> {
// Sync to external system
await externalApi.syncOrder(order);
}

// Before order total is calculated
export async function onOrderCalculate(
context: HookContext,
order: OrderDraft,
totals: OrderTotals
): Promise<OrderTotals> {
// Apply custom discount
if (order.metadata.loyaltyTier === 'gold') {
totals.discount += totals.subtotal * 0.1;
}

return totals;
}

Payment Hooks

// Before payment is processed
export async function onPaymentBeforeProcess(
context: HookContext,
payment: PaymentRequest
): Promise<PaymentRequest | HookError> {
// Add fraud check
const fraudScore = await fraudService.check(payment);
if (fraudScore > 0.8) {
return { error: 'FRAUD_DETECTED', message: 'Payment flagged for review' };
}

return payment;
}

// After payment completes
export async function onPaymentAfterProcess(
context: HookContext,
payment: Payment
): Promise<void> {
// Update external accounting
await accountingService.recordPayment(payment);
}

Inventory Hooks

// Before inventory update
export async function onInventoryBeforeUpdate(
context: HookContext,
update: InventoryUpdate
): Promise<InventoryUpdate> {
// Auto-reorder when low
if (update.newQuantity < update.reorderPoint) {
await supplierApi.createOrder(update.itemId, update.reorderQuantity);
}

return update;
}

Hook Context

Every hook receives a context object:

interface HookContext {
// Tenant information
tenantId: string;
locationId: string;

// User who triggered the action
userId: string;
userRole: string;

// Plugin storage
storage: PluginStorage;

// Logging
logger: Logger;

// Configuration
settings: PluginSettings;

// Platform APIs
api: PlatformAPI;
}

Plugin Storage

Persist data between executions:

// Key-value storage
await context.storage.set('lastSync', Date.now());
const lastSync = await context.storage.get('lastSync');

// Structured storage
await context.storage.setObject('config', {
apiKey: 'xxx',
enabled: true
});
const config = await context.storage.getObject('config');

// List operations
await context.storage.push('processedOrders', orderId);
const orders = await context.storage.list('processedOrders');

// Delete
await context.storage.delete('oldKey');

Storage Limits

PlanStorage LimitOperations/min
Free1 MB100
Pro100 MB1,000
Enterprise1 GB10,000

External API Calls

Make HTTP requests to external services:

import { http } from '@olympus/plugin-sdk';

// GET request
const response = await http.get('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});

// POST request
const result = await http.post('https://api.example.com/webhook', {
body: JSON.stringify({ orderId: order.id }),
headers: {
'Content-Type': 'application/json'
}
});

// Handle errors
try {
const data = await http.get(url);
} catch (error) {
if (error.status === 429) {
// Rate limited
await sleep(1000);
return retry();
}
throw error;
}

HTTP Restrictions

  • HTTPS only (no HTTP)
  • Timeout: 30 seconds max
  • Response size: 10 MB max
  • Rate limit: 100 requests/minute
  • Allowed domains must be declared in manifest

UI Extensions

Add custom UI components:

Settings Screen

// src/ui/settings.tsx
import { SettingsForm, TextInput, Toggle } from '@olympus/plugin-ui';

export function PluginSettings({ settings, onChange }) {
return (
<SettingsForm>
<TextInput
label="API Key"
value={settings.apiKey}
onChange={(value) => onChange({ ...settings, apiKey: value })}
type="password"
/>

<Toggle
label="Enable auto-sync"
value={settings.autoSync}
onChange={(value) => onChange({ ...settings, autoSync: value })}
/>
</SettingsForm>
);
}

Dashboard Widget

// src/ui/widget.tsx
import { Widget, Chart, Metric } from '@olympus/plugin-ui';

export function DashboardWidget({ data }) {
return (
<Widget title="My Plugin Stats">
<Metric
label="Orders Processed"
value={data.ordersProcessed}
trend="+12%"
/>
<Chart
type="line"
data={data.dailyTrend}
/>
</Widget>
);
}
// Add menu item to staff shell
export const menuExtension = {
location: 'staff.sidebar',
item: {
label: 'My Plugin',
icon: 'plugin-icon',
route: '/plugins/my-plugin'
}
};

Testing

Unit Tests

// tests/handlers.test.ts
import { describe, it, expect } from 'vitest';
import { createMockContext } from '@olympus/plugin-testing';
import { onOrderBeforeCreate } from '../src/handlers/order';

describe('Order Handler', () => {
it('should add custom field', async () => {
const context = createMockContext();
const order = { items: [{ id: '1', quantity: 1 }], metadata: {} };

const result = await onOrderBeforeCreate(context, order);

expect(result.metadata.customField).toBe('value');
});

it('should reject empty orders', async () => {
const context = createMockContext();
const order = { items: [], metadata: {} };

const result = await onOrderBeforeCreate(context, order);

expect(result.error).toBe('ORDER_EMPTY');
});
});

Integration Tests

// tests/integration.test.ts
import { PluginTestEnvironment } from '@olympus/plugin-testing';

describe('Plugin Integration', () => {
let env: PluginTestEnvironment;

beforeAll(async () => {
env = await PluginTestEnvironment.create({
manifest: './olympus-plugin.json',
mockData: './tests/fixtures'
});
});

it('should process order correctly', async () => {
const order = await env.createOrder({
items: [{ menuItemId: 'burger', quantity: 2 }]
});

// Plugin hooks are automatically invoked
expect(order.metadata.customField).toBeDefined();
expect(order.totals.discount).toBeGreaterThan(0);
});
});

Local Development

# Start development server with hot reload
npm run dev

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Lint and type check
npm run lint
npm run typecheck

Building & Packaging

Build for Production

# Build WASM and JS bundles
npm run build

# Output:
# dist/
# index.wasm # Main plugin logic
# settings.js # Settings UI
# widget.js # Dashboard widget

Validate Package

# Run validation checks
olympus-plugin validate

# Checks:
# ✓ Manifest schema valid
# ✓ Permissions declared correctly
# ✓ WASM binary under size limit
# ✓ No security issues detected
# ✓ All hooks properly exported

Package for Submission

# Create submission package
olympus-plugin package

# Output: my-plugin-1.0.0.opkg

Publishing

Submission Process

  1. Create Developer Account at developers.olympuscloud.ai
  2. Submit Plugin via dashboard or CLI
  3. Automated Review - Security scan, API compliance
  4. Manual Review - Functionality, UX, policy compliance (1-3 days)
  5. Published - Available in marketplace

Submission Checklist

RequirementDescription
ScreenshotsMinimum 3 showing key features
DocumentationHelp docs or README
Privacy PolicyIf accessing customer data
Support ContactEmail or help desk URL
Test AccountFor reviewer testing
ChangelogWhat's new in this version

Submit via CLI

# Login to developer account
olympus-plugin login

# Submit for review
olympus-plugin submit

# Check submission status
olympus-plugin status

Version Updates

# Bump version
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.0 -> 1.1.0
npm version major # 1.0.0 -> 2.0.0

# Submit update
olympus-plugin submit --update

Best Practices

Performance

PracticeWhy
Minimize storage operationsStorage has latency
Cache external API responsesReduce HTTP calls
Use async/await properlyDon't block the runtime
Keep WASM bundle smallFaster cold starts

Security

PracticeWhy
Never hardcode secretsUse plugin settings
Validate all inputPrevent injection
Sanitize outputPrevent XSS
Use HTTPS onlyEncrypt data in transit
Minimal permissionsPrinciple of least privilege

Error Handling

export async function onOrderBeforeCreate(context, order) {
try {
// Plugin logic
return await processOrder(order);
} catch (error) {
// Log error for debugging
context.logger.error('Order processing failed', { error, orderId: order.id });

// Return graceful error (doesn't block order)
return order;

// OR: Block order with error
// return { error: 'PROCESSING_FAILED', message: 'Please try again' };
}
}

API Reference

REST API Documentation

See complete API reference at:

Quick Reference

import {
// Hook types
HookContext,
HookResult,

// Data types
Order,
Payment,
Customer,

// Utilities
http,
crypto,

// UI components
SettingsForm,
Widget,

} from '@olympus/plugin-sdk';

Support

Resources

ResourceURL
Developer Docsdevelopers.olympuscloud.ai/docs
API Referencedevelopers.olympuscloud.ai/api
Community Forumdevelopers.olympuscloud.ai/community
GitHub Examplesgithub.com/olympuscloud/plugin-examples
Office HoursThursdays 2pm PT

Getting Help

  • Community Forum: General questions
  • GitHub Issues: Bug reports
  • Email: developers@olympuscloud.ai
  • Priority Support: Partner tier developers