| path | docs/api/Widget_API.mdx | ||||
|---|---|---|---|---|---|
| title | Widget Management API | ||||
| description | Complete API reference for managing widgets in SveltyCMS including installation, activation, and dependency management. | ||||
| order | 6 | ||||
| icon | mdi:puzzle | ||||
| author | admin | ||||
| created | 2025-10-05 | ||||
| updated | 2025-10-05 | ||||
| tags |
|
The Widget API provides comprehensive endpoints for managing widgets (plugins/extensions) in SveltyCMS. Widgets extend functionality and can be core (built-in) or custom (user-installed).
Base Path: /api/widgets
All widget endpoints require authentication:
Cookie: session=your-session-idPermissions Required: api:widgets for all endpoints
SveltyCMS uses a 3-pillar widget architecture:
- Definition (index.ts) - Widget metadata, validation schema, and configuration
- Input (Input.svelte) - Edit/creation interface component
- Display (Display.svelte) - Read-only display component
Widget Types:
core- Built-in widgets in/src/widgets/core/custom- User-created widgets in/src/widgets/custom/marketplace- Runtime-installed widgets (planned feature)
Retrieves comprehensive information about all available widgets.
GET /api/widgets/listQuery Parameters:
tenantId(string, optional) - Filter by tenant (multi-tenant mode)category(string, optional) - Filter by widget categorystatus(string, optional) - Filter by status (active,inactive,core)
Headers:
Cookie: session=your-session-idPermissions Required: api:widgets
Success (200):
{
"success": true,
"widgets": {
"widgetFunction": {
"name": "widgetFunction",
"version": "1.0.0",
"description": "Core widget function component",
"category": "core",
"status": "active",
"isCore": true,
"dependencies": [],
"metadata": {
"author": "SveltyCMS",
"license": "MIT",
"icon": "mdi:puzzle"
}
},
"mediaUpload": {
"name": "mediaUpload",
"version": "1.2.0",
"description": "Media file upload widget",
"category": "media",
"status": "active",
"isCore": true,
"dependencies": ["widgetFunction"],
"metadata": {
"author": "SveltyCMS",
"license": "MIT",
"icon": "mdi:upload"
}
}
},
"summary": {
"total": 25,
"active": 18,
"inactive": 5,
"core": 15,
"custom": 10
},
"tenantId": "default-tenant",
"processingTime": "45ms"
}Error Responses:
// 401 Unauthorized
{
"success": false,
"message": "Unauthorized"
}
// 403 Forbidden
{
"success": false,
"message": "Insufficient permissions"
}Installs a widget from the marketplace.
POST /api/widgets/installHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"widgetId": "custom-widget-123",
"tenantId": "tenant-xyz"
}Permissions Required: api:widgets
Success (200):
{
"success": true,
"widgetId": "custom-widget-123",
"tenantId": "tenant-xyz",
"installedAt": "2025-10-05T14:30:00Z",
"message": "Widget installed successfully"
}Note: This endpoint currently provides a mock implementation. The actual installation logic is planned for future development.
Error Responses:
// 400 Bad Request - Missing widget ID
{
"error": "Widget ID is required"
}
// 401 Unauthorized
{
"error": "Unauthorized"
}
// 403 Forbidden
{
"error": "Insufficient permissions"
}
// 500 Internal Server Error
{
"error": "Failed to install widget: <error message>"
}Uninstalls a previously installed widget.
POST /api/widgets/uninstallHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"widgetName": "custom-widget",
"tenantId": "tenant-xyz",
"force": false
}Parameters:
widgetName(string, required) - Name of widget to uninstalltenantId(string, optional) - Tenant ID (defaults to user's tenant)force(boolean, optional) - Force uninstall even with dependencies
Permissions Required: api:widgets
Success (200):
{
"success": true,
"widgetName": "custom-widget",
"tenantId": "tenant-xyz",
"uninstalledAt": "2025-10-05T14:30:00Z",
"message": "Widget uninstalled successfully"
}Error Responses:
// 400 Bad Request - Missing widget name
{
"success": false,
"message": "Widget name is required"
}
// 400 Bad Request - Has dependencies
{
"success": false,
"message": "Cannot uninstall: Other widgets depend on this widget",
"dependents": ["widget1", "widget2"]
}
// 409 Conflict - Core widget
{
"success": false,
"message": "Cannot uninstall core widget"
}Activates an installed but inactive widget.
POST /api/widgets/activateHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"widgetName": "custom-widget",
"tenantId": "tenant-xyz"
}Permissions Required: api:widgets
Success (200):
{
"success": true,
"widgetName": "custom-widget",
"status": "active",
"message": "Widget activated successfully"
}Deactivates an active widget without uninstalling it.
POST /api/widgets/deactivateHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"widgetName": "custom-widget",
"tenantId": "tenant-xyz"
}Permissions Required: api:widgets
Success (200):
{
"success": true,
"widgetName": "custom-widget",
"status": "inactive",
"message": "Widget deactivated successfully"
}Validates a widget's integrity and compatibility.
POST /api/widgets/validateHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"widgetName": "custom-widget",
"tenantId": "tenant-xyz"
}Permissions Required: api:widgets
Success (200):
{
"success": true,
"valid": true,
"checks": {
"fileIntegrity": "passed",
"dependencies": "passed",
"compatibility": "passed",
"permissions": "passed"
},
"message": "Widget validation successful"
}Validation Failure (200 with warnings):
{
"success": true,
"valid": false,
"checks": {
"fileIntegrity": "passed",
"dependencies": "failed",
"compatibility": "warning",
"permissions": "passed"
},
"errors": ["Missing dependency: widgetFunction v2.0.0"],
"warnings": ["Newer version available: 1.3.0"]
}Synchronizes widget registry with filesystem.
POST /api/widgets/syncHeaders:
Cookie: session=your-session-id
Content-Type: application/jsonBody:
{
"tenantId": "tenant-xyz",
"force": false
}Permissions Required: api:widgets
Success (200):
{
"success": true,
"syncedAt": "2025-10-05T14:30:00Z",
"changes": {
"added": 2,
"removed": 1,
"updated": 3
},
"message": "Widget registry synchronized"
}Retrieves widgets currently used by collections (cannot be deactivated).
GET /api/widgets/requiredHeaders:
Cookie: session=your-session-idPermissions Required: Not required (uses locals)
Success (200):
{
"requiredWidgets": ["String", "RichText", "Media", "Relation"],
"collectionsAnalyzed": 5,
"tenantId": "default-tenant"
}Retrieves currently active widgets with 3-pillar architecture metadata.
GET /api/widgets/activeQuery Parameters:
refresh(boolean, optional) - Force cache refresh (?refresh=true)
Headers:
Cookie: session=your-session-idPermissions Required: Not required (uses locals)
Success (200):
{
"widgets": [
{
"name": "String",
"isCore": true,
"icon": "mdi:text",
"description": "Single-line text input widget",
"inputComponentPath": "/src/widgets/core/string/Input.svelte",
"displayComponentPath": "/src/widgets/core/string/Display.svelte",
"dependencies": []
}
],
"tenantId": "default-tenant"
}- Installation - Download and install widget files
- Validation - Check integrity, dependencies, compatibility
- Registration - Add to widget registry
- Activation - Enable widget functionality
- Usage - Widget is active and functional
- Deactivation - Disable without removing
- Uninstallation - Remove widget completely
Widgets can depend on other widgets:
{
"name": "imageGallery",
"dependencies": ["widgetFunction", "mediaUpload"],
"dependents": ["portfolioWidget"]
}Dependency Rules:
- Cannot uninstall widget if others depend on it
- Dependencies must be installed before dependent
- Circular dependencies are not allowed
The Widget API uses permission-based authorization (no direct DB queries):
// Permission check only - no database operations
const hasPermission = hasPermissionWithRoles(user, 'api:widgets', roles);Widget data is stored in:
- File System - Widget code and assets
- Widget Store - Runtime registry (Svelte store)
- Configuration - Settings and metadata
When multi-tenant mode is enabled:
- Each tenant has isolated widget installations
- Core widgets are shared across tenants
- Custom widgets are tenant-specific
- Widget settings are tenant-scoped
Tenant-Scoped Installation:
POST /api/widgets/install
Content-Type: application/json
{
"widgetId": "custom-widget",
"tenantId": "tenant-abc"
}Widgets are organized by category:
- Core - Essential system widgets
- Media - Media management widgets
- Content - Content creation/editing widgets
- User - User management widgets
- Analytics - Analytics and reporting widgets
- Integration - Third-party integrations
- Custom - User-created widgets
- Only users with
api:widgetspermission can manage widgets - Widget installation requires admin rights
- Tenant isolation in multi-tenant mode
- Code signing verification
- Dependency validation
- Permission declaration
- Sandbox execution (planned)
- Official marketplace (verified)
- Third-party marketplace (community)
- Local installation (custom)
my-widget/
├── index.ts # Widget definition, validation schema, metadata
├── Input.svelte # Edit/creation interface
├── Display.svelte # Read-only display component
└── types.ts # TypeScript types (optional)
import { createWidget } from '@src/widgets/factory';
import { object, string } from 'valibot';
import * as m from '@src/paraglide/messages';
const validationSchema = object({
value: string()
});
const MyWidget = createWidget({
Name: 'MyWidget',
Icon: 'mdi:puzzle',
Description: m.widget_mywidget_description(),
inputComponentPath: '/src/widgets/custom/mywidget/Input.svelte',
displayComponentPath: '/src/widgets/custom/mywidget/Display.svelte',
validationSchema,
defaults: { placeholder: 'Enter value' },
GuiSchema: {
/* ... */
},
aggregations: {
/* ... */
},
GraphqlSchema: () => ({ typeID: 'String', graphql: '' })
});
export default MyWidget;// List all widgets
const response = await fetch('/api/widgets/list', {
credentials: 'include'
});
const { widgets, summary } = await response.json();
// Install widget
const installResponse = await fetch('/api/widgets/install', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
widgetId: 'custom-widget-123',
tenantId: 'my-tenant'
})
});
// Activate widget
await fetch('/api/widgets/activate', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
widgetName: 'custom-widget'
})
});For implementation details, see:
src/routes/api/widgets/list/+server.ts- List widgetssrc/routes/api/widgets/install/+server.ts- Install endpointsrc/routes/api/widgets/uninstall/+server.ts- Uninstall endpointsrc/stores/widgetStore.svelte.ts- Widget registry storesrc/databases/auth/permissions.ts- Permission checking