Quick Overview
Section titled “Quick Overview”- Modules live in
/modules - Product type modules should start with "Service" (e.g.,
Servicehosting) - Module IDs must be lowercase
- Modules can add: API endpoints, templates, product types, cron jobs, hooks, and more
Directory Structure
Section titled “Directory Structure”Module names start with a capital letter:
✅ Valid: Example, Mynewmodule, Servicehosting ❌ Invalid: example, MyNewModule, servicehosting
See the file structure docs for the complete layout.
Example Module
Section titled “Example Module”The best way to learn is by example. Our example module demonstrates:
- Admin and client area templates
- Email templates
- API routes
- Using the DI container
- Routes with variables (
/example/user/:id) - Translations
- Database interaction
- Event hooks
Running Scheduled Tasks
Section titled “Running Scheduled Tasks”Hook into cron using onBeforeAdminCronRun or onAfterAdminCronRun:
public static function onBeforeAdminCronRun(\Box_Event $event): void{ error_log('Cron was called!');}Module Permissions
Section titled “Module Permissions”Define custom permissions for staff members in your module.
Setting Up Permissions
Section titled “Setting Up Permissions”public function getModulePermissions(): array{ return [ 'delete_something' => [ 'type' => 'bool', 'display_name' => __trans('Delete something'), 'description' => __trans('Allows the staff member to delete "something"'), ], 'can_always_access' => true, 'manage_settings' => [], ];}can_always_access grants baseline access to the module, while manage_settings limits the settings page to the listed permissions.
Checking Permissions
Section titled “Checking Permissions”Quick check:
$this->di['mod_service']('Staff')->checkPermissionsAndThrowException('example', 'delete_something');Manual check:
$staff_service = $this->di['mod_service']('Staff');if (!$staff_service->hasPermission(null, 'example', 'delete_something')) { throw new \FOSSBilling\InformationException( 'You do not have permission to perform this action', [], 403 );}Key points:
- Pass
nullto check the currently logged-in staff member - Use your module ID as the second parameter
- Permission keys are module-specific (no naming conflicts)
Getting Started
Section titled “Getting Started”- Copy the example module
- Rename files and update the manifest
- Start building your functionality
- Test in a development environment
Join our Discord if you need help.