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 dependency injection container
- Routes with variables (
/example/user/:id) - Translations
- Interacting with the database
- Event hooks
For template-specific helpers, see Twig Filters & Functions. For browser-side API forms and links, see the JavaScript API Wrapper.
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!');}See Event Hooks for the full list of hook names.
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 );}Twig templates:
{\% if has_permission('example', 'delete_something') %} <button type="button">{{ 'Delete something'|trans }}</button>{\% endif %}Remove the backslashes when using this in your actual template.
Use the has_permission(module, permission = null) Twig function in admin templates to check whether the logged-in staff member has access to a module or module permission. Omit the second argument to check module access only, for example has_permission('example'). The function returns false when no admin is logged in or the permission check fails.
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)
- Use
has_permissionto conditionally render admin UI, but keep enforcing permissions in PHP/API handlers
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.