activepieces
AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents
Crawler Summary
Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever a user needs to create, modify, debug, or audit any WHMCS module, hook, or integration. --- name: whmcs-dev-skills description: > Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever Capability contract not published. No trust telemetry is available yet. Last updated 4/15/2026.
Freshness
Last checked 4/15/2026
Best For
whmcs-dev-skills is best for abort, be, verify workflows where OpenClaw compatibility matters.
Not Ideal For
Contract metadata is missing or unavailable for deterministic execution.
Evidence Sources Checked
editorial-content, GITHUB OPENCLEW, runtime-metrics, public facts pack
Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever a user needs to create, modify, debug, or audit any WHMCS module, hook, or integration. --- name: whmcs-dev-skills description: > Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever
Public facts
4
Change events
1
Artifacts
0
Freshness
Apr 15, 2026
Capability contract not published. No trust telemetry is available yet. Last updated 4/15/2026.
Trust score
Unknown
Compatibility
OpenClaw
Freshness
Apr 15, 2026
Vendor
Therabbirifat
Artifacts
0
Benchmarks
0
Last release
Unpublished
Key links, install path, and a quick operational read before the deeper crawl record.
Summary
Capability contract not published. No trust telemetry is available yet. Last updated 4/15/2026.
Setup snapshot
git clone https://github.com/TheRabbiRifat/whmcs-dev-skills.gitSetup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.
Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data.
Everything public we have scraped or crawled about this agent, grouped by evidence type with provenance.
Vendor
Therabbirifat
Protocol compatibility
OpenClaw
Handshake status
UNKNOWN
Crawlable docs
6 indexed pages on the official domain
Merged public release, docs, artifact, benchmark, pricing, and trust refresh events.
Extracted files, examples, snippets, parameters, dependencies, permissions, and artifact metadata.
Extracted files
0
Examples
6
Snippets
0
Languages
typescript
Parameters
text
✓ Use <?php ?> full tags only (never <? ?>). ✓ Omit closing ?> in pure-PHP files. ✓ Indent with 4 spaces (no tabs). ✓ No trailing whitespace on lines. ✓ Use Unix line endings (\n), not Windows (\r\n). ✓ End every file with a single newline. ✓ UTF-8 encoding without BOM. ✓ Follow PSR-1 (Basic Coding Standard) and PSR-2 (Coding Style Guide). ✓ Use strict_types declaration: declare(strict_types=1); ✓ Use PHP 8.1+ type hints for all parameters, return types, and properties.
php
<?php
use Illuminate\Database\Capsule\Manager as Capsule;
// SELECT
$clients = Capsule::table('tblclients')
->where('status', '=', 'Active')
->orderBy('lastname', 'asc')
->get();
// SELECT single row
$client = Capsule::table('tblclients')
->where('id', '=', $clientId)
->first();
// INSERT
Capsule::table('mod_mymodule_logs')->insert([
'client_id' => $clientId,
'action' => 'provisioned',
'created_at' => date('Y-m-d H:i:s'),
]);
// UPDATE
Capsule::table('tblhosting')
->where('id', '=', $serviceId)
->update(['notes' => 'Updated via module']);
// DELETE (use with caution)
Capsule::table('mod_mymodule_logs')
->where('created_at', '<', $cutoffDate)
->delete();php
<?php
use Illuminate\Database\Capsule\Manager as Capsule;
try {
Capsule::schema()->create('mod_mymodule_data', function ($table) {
/** @var \Illuminate\Database\Schema\Blueprint $table */
$table->increments('id');
$table->unsignedInteger('client_id');
$table->string('key', 255);
$table->text('value')->nullable();
$table->timestamps();
$table->index('client_id');
$table->unique(['client_id', 'key']);
});
} catch (\Exception $e) {
return [
'status' => 'error',
'description' => 'Unable to create table: ' . $e->getMessage(),
];
}php
<?php
use Illuminate\Database\Capsule\Manager as Capsule;
function mymodule_upgrade($vars)
{
$currentVersion = $vars['version'];
if (version_compare($currentVersion, '1.1.0', '<')) {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->string('status', 50)->default('active')->after('value');
});
}
if (version_compare($currentVersion, '1.2.0', '<')) {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->unsignedInteger('order_id')->nullable()->after('client_id');
$table->index('order_id');
});
}
}php
// ❌ NEVER DO THIS
$result = mysql_query("SELECT * FROM tblclients WHERE id = $clientId");
$result = mysqli_query($conn, "SELECT * FROM tblclients WHERE id = '$clientId'");
$result = full_query("SELECT * FROM tblclients WHERE id = " . $_GET['id']);php
<?php
defined("WHMCS") or die("Access Denied");
use Illuminate\Database\Capsule\Manager as Capsule;
use WHMCS\Module\Addon\Setting;
function mymodule_config(): array
{
return [
'name' => 'My Module',
'description' => 'A brief description of what this module does.',
'version' => '1.0.0',
'author' => 'Your Name',
'language' => 'english',
'fields' => [
'apiUrl' => [
'FriendlyName' => 'API URL',
'Type' => 'text',
'Size' => '60',
'Default' => 'https://api.example.com',
'Description' => 'The base URL of the external API.',
],
'apiKey' => [
'FriendlyName' => 'API Key',
'Type' => 'password',
'Size' => '40',
'Description' => 'Your API authentication key.',
],
'enableDebug' => [
'FriendlyName' => 'Debug Mode',
'Type' => 'yesno',
'Description' => 'Enable verbose logging for troubleshooting.',
],
'syncInterval' => [
'FriendlyName' => 'Sync Interval',
'Type' => 'dropdown',
'Options' => '1 Hour,6 Hours,12 Hours,24 Hours',
'Default' => '6 Hours',
'Description' => 'How often to sync data.',
],
],
];
}Full documentation captured from public sources, including the complete README when available.
Docs source
GITHUB OPENCLEW
Editorial quality
ready
Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever a user needs to create, modify, debug, or audit any WHMCS module, hook, or integration. --- name: whmcs-dev-skills description: > Senior WHMCS Developer & Architect skill for AI coding agents. Builds, debugs, and maintains WHMCS Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules, and Action Hooks. Enforces WHMCS 8.x / 9.x best practices, modern PHP 8.1+ standards, Laravel Capsule ORM, Smarty v4 templating, and PSR-1/PSR-2 coding style. Use this skill whenever
Scope: Full-stack WHMCS module development covering Addon Modules, Provisioning (Server) Modules, Domain Registrar Modules, Payment Gateway Modules (Third-Party, Merchant, Tokenised), Action Hooks, Internal/External API integration, and Theme/Template customisation.
defined("WHMCS") or die("Access Denied"); as the first line of
every PHP file.Illuminate\Database\Capsule\Manager (Laravel Capsule) for all
database operations.logModuleCall() for every external API request to enable the
WHMCS Module Log (Configuration → System Logs → Module Log).logActivity() to write meaningful entries to the System Activity Log..tpl template files for all HTML output — never echo raw
HTML in logic files.<?php full opening tags; omit the closing ?> tag in pure-PHP files.try/catch
blocks.$_POST and $_GET input.mod_ (e.g., mod_yourmodule_data).lang/english.php language file for every module.DROP TABLE operations in deactivation functions./admin/, /includes/, /vendor/). Use Hooks
or Modules instead.configuration.php directly.mysql_*, mysqli_*, or raw PDO — always use Capsule.{php} tags in Smarty templates.$_REQUEST — be explicit with $_POST or $_GET.ROOTDIR, $CONFIG['SystemURL'], or
WHMCS constants.encrypt() / decrypt() helpers.echo or print for output in module files — return structured arrays
or use Smarty templates.| Component | WHMCS 8.x (8.11+) | WHMCS 9.x | |--------------------|--------------------------|--------------------------| | PHP | 8.1 min, 8.2 recommended | 8.2 min, 8.3 recommended | | ionCube Loader | 13.0.2+ | 13.0.2+ (14.4.0 rec.) | | Smarty | v3.1.x | v4.3.4 | | GuzzleHTTP | v7.4 | v7.4.5 | | Illuminate | v7.x | v9.0 | | MySQL/MariaDB | 5.7+ / 10.2+ | 8.0+ / 10.6+ |
Based on the WHMCS Style Guide:
✓ Use <?php ?> full tags only (never <? ?>).
✓ Omit closing ?> in pure-PHP files.
✓ Indent with 4 spaces (no tabs).
✓ No trailing whitespace on lines.
✓ Use Unix line endings (\n), not Windows (\r\n).
✓ End every file with a single newline.
✓ UTF-8 encoding without BOM.
✓ Follow PSR-1 (Basic Coding Standard) and PSR-2 (Coding Style Guide).
✓ Use strict_types declaration: declare(strict_types=1);
✓ Use PHP 8.1+ type hints for all parameters, return types, and properties.
| Element | Convention | Example |
|---------------------|--------------------------------|----------------------------------|
| Module Directory | lowercase, letters & numbers | mymodule or my_module |
| Module Functions | {modulename}_FunctionName | mymodule_config() |
| Hook Functions | Unique prefixed name | mymodule_hookClientAdd() |
| Database Tables | mod_{modulename}_{entity} | mod_mymodule_settings |
| Config Fields | camelCase keys | apiKey, secretToken |
| Template Files | lowercase with hyphens | admin-dashboard.tpl |
| Language Keys | snake_case | module_description |
<?php
use Illuminate\Database\Capsule\Manager as Capsule;
// SELECT
$clients = Capsule::table('tblclients')
->where('status', '=', 'Active')
->orderBy('lastname', 'asc')
->get();
// SELECT single row
$client = Capsule::table('tblclients')
->where('id', '=', $clientId)
->first();
// INSERT
Capsule::table('mod_mymodule_logs')->insert([
'client_id' => $clientId,
'action' => 'provisioned',
'created_at' => date('Y-m-d H:i:s'),
]);
// UPDATE
Capsule::table('tblhosting')
->where('id', '=', $serviceId)
->update(['notes' => 'Updated via module']);
// DELETE (use with caution)
Capsule::table('mod_mymodule_logs')
->where('created_at', '<', $cutoffDate)
->delete();
_activate)<?php
use Illuminate\Database\Capsule\Manager as Capsule;
try {
Capsule::schema()->create('mod_mymodule_data', function ($table) {
/** @var \Illuminate\Database\Schema\Blueprint $table */
$table->increments('id');
$table->unsignedInteger('client_id');
$table->string('key', 255);
$table->text('value')->nullable();
$table->timestamps();
$table->index('client_id');
$table->unique(['client_id', 'key']);
});
} catch (\Exception $e) {
return [
'status' => 'error',
'description' => 'Unable to create table: ' . $e->getMessage(),
];
}
_upgrade)<?php
use Illuminate\Database\Capsule\Manager as Capsule;
function mymodule_upgrade($vars)
{
$currentVersion = $vars['version'];
if (version_compare($currentVersion, '1.1.0', '<')) {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->string('status', 50)->default('active')->after('value');
});
}
if (version_compare($currentVersion, '1.2.0', '<')) {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->unsignedInteger('order_id')->nullable()->after('client_id');
$table->index('order_id');
});
}
}
// ❌ NEVER DO THIS
$result = mysql_query("SELECT * FROM tblclients WHERE id = $clientId");
$result = mysqli_query($conn, "SELECT * FROM tblclients WHERE id = '$clientId'");
$result = full_query("SELECT * FROM tblclients WHERE id = " . $_GET['id']);
Path: /modules/addons/{modulename}/
Official Sample: github.com/WHMCS/sample-addon-module
| Function | Purpose |
|------------------------------|------------------------------------------|
| {name}_config() | Module metadata + configuration fields |
| {name}_activate() | Runs on module activation (create tables)|
| {name}_deactivate() | Runs on module deactivation (cleanup) |
| {name}_output($vars) | Admin area page output (echo, not return)|
| {name}_clientarea($vars) | Client area output (return array) |
| {name}_upgrade($vars) | Handles version upgrades (schema changes)|
<?php
defined("WHMCS") or die("Access Denied");
use Illuminate\Database\Capsule\Manager as Capsule;
use WHMCS\Module\Addon\Setting;
function mymodule_config(): array
{
return [
'name' => 'My Module',
'description' => 'A brief description of what this module does.',
'version' => '1.0.0',
'author' => 'Your Name',
'language' => 'english',
'fields' => [
'apiUrl' => [
'FriendlyName' => 'API URL',
'Type' => 'text',
'Size' => '60',
'Default' => 'https://api.example.com',
'Description' => 'The base URL of the external API.',
],
'apiKey' => [
'FriendlyName' => 'API Key',
'Type' => 'password',
'Size' => '40',
'Description' => 'Your API authentication key.',
],
'enableDebug' => [
'FriendlyName' => 'Debug Mode',
'Type' => 'yesno',
'Description' => 'Enable verbose logging for troubleshooting.',
],
'syncInterval' => [
'FriendlyName' => 'Sync Interval',
'Type' => 'dropdown',
'Options' => '1 Hour,6 Hours,12 Hours,24 Hours',
'Default' => '6 Hours',
'Description' => 'How often to sync data.',
],
],
];
}
<?php
function mymodule_activate(): array
{
try {
Capsule::schema()->create('mod_mymodule_data', function ($table) {
/** @var \Illuminate\Database\Schema\Blueprint $table */
$table->increments('id');
$table->unsignedInteger('client_id');
$table->string('key', 255);
$table->text('value')->nullable();
$table->timestamps();
$table->index('client_id');
});
return [
'status' => 'success',
'description' => 'Module activated successfully. Configure settings above.',
];
} catch (\Exception $e) {
return [
'status' => 'error',
'description' => 'Failed to create database table: ' . $e->getMessage(),
];
}
}
function mymodule_deactivate(): array
{
try {
Capsule::schema()->dropIfExists('mod_mymodule_data');
return [
'status' => 'success',
'description' => 'Module deactivated and data cleaned up.',
];
} catch (\Exception $e) {
return [
'status' => 'error',
'description' => 'Failed to remove database table: ' . $e->getMessage(),
];
}
}
<?php
function mymodule_output($vars): void
{
$moduleLink = $vars['modulelink'];
$version = $vars['version'];
$apiUrl = $vars['apiUrl'];
$apiKey = $vars['apiKey'];
$LANG = $vars['_lang'];
$action = $_GET['action'] ?? 'index';
switch ($action) {
case 'settings':
// Settings page
echo '<h2>' . ($LANG['settings_title'] ?? 'Settings') . '</h2>';
break;
default:
// Dashboard / index page
echo '<h2>' . ($LANG['dashboard_title'] ?? 'Dashboard') . '</h2>';
echo '<p>Module Version: ' . htmlspecialchars($version) . '</p>';
echo '<a href="' . $moduleLink . '&action=settings" '
. 'class="btn btn-default">Settings</a>';
break;
}
}
<?php
function mymodule_clientarea($vars): array
{
$moduleLink = $vars['modulelink'];
$LANG = $vars['_lang'];
return [
'pagetitle' => $LANG['client_page_title'] ?? 'My Module',
'breadcrumb' => [
'index.php?m=mymodule' => 'My Module',
],
'templatefile' => 'views/client/dashboard',
'requirelogin' => true,
'forcessl' => true,
'vars' => [
'moduleLink' => $moduleLink,
'data' => Capsule::table('mod_mymodule_data')
->where('client_id', '=', $_SESSION['uid'] ?? 0)
->get()
->toArray(),
],
];
}
Path: /modules/servers/{modulename}/
Official Sample: github.com/WHMCS/sample-provisioning-module
| Function | Required | Purpose |
|-----------------------------------|----------|------------------------------------------|
| {name}_MetaData() | Yes | Module metadata and capabilities |
| {name}_ConfigOptions() | Yes | Product configuration fields |
| {name}_CreateAccount($params) | Yes | Provision a new service |
| {name}_SuspendAccount($params) | Yes | Suspend an overdue service |
| {name}_UnsuspendAccount($params)| Yes | Unsuspend after payment |
| {name}_TerminateAccount($params)| Yes | Terminate a service |
| {name}_Renew($params) | Optional | Handle renewal payments |
| {name}_ChangePassword($params) | Optional | Client-initiated password change |
| {name}_ChangePackage($params) | Optional | Handle upgrades/downgrades |
| {name}_TestConnection($params) | Optional | Test server connectivity from admin |
| {name}_ClientArea($params) | Optional | Client area output for the service |
| {name}_AdminLink($params) | Optional | Quick-link on server config page |
| {name}_LoginLink($params) | Optional | Login link on product management page |
| {name}_UsageUpdate($params) | Optional | Daily disk/bandwidth import |
| {name}_AdminCustomButtonArray()| Optional | Custom admin action buttons |
| {name}_ClientAreaCustomButtonArray()| Optional | Custom client action buttons |
| {name}_ClientAreaAllowedFunctions()| Optional | Hidden callable client functions |
| {name}_AdminServicesTabFields($params)| Optional | Extra fields on admin services tab |
| {name}_AdminServicesTabFieldsSave($params)| Optional | Save extra admin fields |
<?php
function myserver_CreateAccount(array $params): string
{
try {
$apiUrl = $params['serverhostname'];
$apiUser = $params['serverusername'];
$apiPass = $params['serverpassword'];
$domain = $params['domain'];
$username = $params['username'];
$password = $params['password'];
$package = $params['configoption1'];
// Make API call to create account on remote server
$response = myserver_apiCall($apiUrl, 'createAccount', [
'domain' => $domain,
'username' => $username,
'password' => $password,
'package' => $package,
]);
// Log the module call for debugging
logModuleCall(
'myserver',
'CreateAccount',
['domain' => $domain, 'username' => $username, 'package' => $package],
$response,
null,
[$apiPass] // Scrub password from logs
);
if ($response['success']) {
return 'success';
}
return 'Error: ' . ($response['message'] ?? 'Unknown error');
} catch (\Exception $e) {
logModuleCall('myserver', 'CreateAccount', $params, $e->getMessage());
return 'Error: ' . $e->getMessage();
}
}
$params Variables (Provisioning)$params['serverhostname'] // Server hostname
$params['serverusername'] // Server username
$params['serverpassword'] // Server password (use decrypt() if needed)
$params['serveraccesshash'] // Server access hash
$params['serversecure'] // SSL/TLS enabled (true/false)
$params['serverport'] // Server port
$params['domain'] // Client's domain
$params['username'] // Service username
$params['password'] // Service password
$params['clientsdetails'] // Array of client information
$params['configoption1'] // First config option value
$params['configoption2'] // Second config option value
$params['customfields'] // Custom field values
$params['serviceid'] // Service ID in tblhosting
$params['pid'] // Product ID
$params['model'] // Service model object (WHMCS 8+)
Path: /modules/registrars/{modulename}/
Official Sample: github.com/WHMCS/sample-registrar-module
| Function | Required | Purpose |
|------------------------------------|----------|--------------------------------------|
| {name}_getConfigArray() | Yes | Configuration fields |
| {name}_RegisterDomain($params) | Yes | Register a new domain |
| {name}_TransferDomain($params) | Yes | Initiate domain transfer |
| {name}_RenewDomain($params) | Yes | Renew an existing domain |
| {name}_GetNameservers($params) | Yes | Retrieve current nameservers |
| {name}_SaveNameservers($params) | Yes | Update nameservers |
| {name}_GetContactDetails($params)| Optional| Get WHOIS contact info |
| {name}_SaveContactDetails($params)| Optional| Update WHOIS contact info |
| {name}_GetEPPCode($params) | Optional | Retrieve EPP/auth code |
| {name}_GetRegistrarLock($params) | Optional | Check registrar lock status |
| {name}_SaveRegistrarLock($params)| Optional | Toggle registrar lock |
| {name}_GetDNS($params) | Optional | Get DNS host records |
| {name}_SaveDNS($params) | Optional | Update DNS host records |
| {name}_GetEmailForwarding($params)| Optional | Get email forwarding rules |
| {name}_SaveEmailForwarding($params)| Optional | Update email forwarding |
| {name}_IDProtectToggle($params) | Optional | Toggle ID Protection |
| {name}_RegisterNameserver($params)| Optional | Create private nameserver |
| {name}_ModifyNameserver($params) | Optional | Update private nameserver |
| {name}_DeleteNameserver($params) | Optional | Remove private nameserver |
| {name}_Sync($params) | Optional | Domain sync (status + expiry) |
| {name}_TransferSync($params) | Optional | Transfer status sync |
| {name}_CheckAvailability($params)| Optional | Domain availability check |
| {name}_GetDomainSuggestions($params)| Optional | Domain name suggestions |
| {name}_RequestDelete($params) | Optional | Delete/cancel domain |
<?php
function myregistrar_RegisterDomain(array $params): array
{
$sld = $params['sld'];
$tld = $params['tld'];
$domain = $sld . '.' . $tld;
$regPeriod = $params['regperiod'];
// Nameservers
$ns1 = $params['ns1'];
$ns2 = $params['ns2'];
$ns3 = $params['ns3'] ?? '';
$ns4 = $params['ns4'] ?? '';
$ns5 = $params['ns5'] ?? '';
// Contact details
$firstName = $params['firstname'];
$lastName = $params['lastname'];
$email = $params['email'];
// ... additional contact fields
try {
$response = myregistrar_apiCall('registerDomain', [
'domain' => $domain,
'period' => $regPeriod,
'ns' => array_filter([$ns1, $ns2, $ns3, $ns4, $ns5]),
'contacts' => [
'firstName' => $firstName,
'lastName' => $lastName,
'email' => $email,
],
]);
logModuleCall('myregistrar', 'RegisterDomain', [
'domain' => $domain, 'period' => $regPeriod,
], $response);
if ($response['success']) {
return ['success' => true];
}
return ['error' => $response['message'] ?? 'Registration failed'];
} catch (\Exception $e) {
logModuleCall('myregistrar', 'RegisterDomain', $params, $e->getMessage());
return ['error' => $e->getMessage()];
}
}
<?php
function myregistrar_Sync(array $params): array
{
$domain = $params['sld'] . '.' . $params['tld'];
try {
$response = myregistrar_apiCall('getDomainInfo', ['domain' => $domain]);
logModuleCall('myregistrar', 'Sync', ['domain' => $domain], $response);
return [
'active' => ($response['status'] === 'active'),
'cancelled' => ($response['status'] === 'cancelled'),
'transferredAway' => ($response['status'] === 'transferred'),
'expirydate' => $response['expiryDate'], // Format: YYYY-MM-DD
];
} catch (\Exception $e) {
logModuleCall('myregistrar', 'Sync', $params, $e->getMessage());
return ['error' => $e->getMessage()];
}
}
Path: /modules/gateways/{modulename}.php
Callback Path: /modules/gateways/callback/{modulename}.php
Official Samples:
| Type | User Experience | Key Function |
|-------------------|----------------------------------------------|------------------|
| Third-Party | Customer leaves site to pay, returns after | _link() |
| Merchant | Card details entered in WHMCS, processed BG | _capture() |
| Tokenised | Card details not stored locally, token used | _storeremote() |
<?php
function mygateway_link(array $params): string
{
// Gateway configuration
$merchantId = $params['merchantId'];
$secretKey = $params['secretKey'];
// Invoice details
$invoiceId = $params['invoiceid'];
$amount = $params['amount'];
$currency = $params['currency'];
$description = $params['description'];
// Client details
$firstName = $params['clientdetails']['firstname'];
$lastName = $params['clientdetails']['lastname'];
$email = $params['clientdetails']['email'];
// System URLs
$systemUrl = $params['systemurl'];
$returnUrl = $params['returnurl'];
$callbackUrl = $systemUrl . 'modules/gateways/callback/mygateway.php';
// Build the payment form
$htmlOutput = '<form method="POST" action="https://pay.example.com/checkout">';
$htmlOutput .= '<input type="hidden" name="merchant_id" value="' . $merchantId . '">';
$htmlOutput .= '<input type="hidden" name="amount" value="' . $amount . '">';
$htmlOutput .= '<input type="hidden" name="currency" value="' . $currency . '">';
$htmlOutput .= '<input type="hidden" name="invoice_id" value="' . $invoiceId . '">';
$htmlOutput .= '<input type="hidden" name="return_url" value="' . $returnUrl . '">';
$htmlOutput .= '<input type="hidden" name="callback_url" value="' . $callbackUrl . '">';
$htmlOutput .= '<input type="hidden" name="email" value="' . $email . '">';
$htmlOutput .= '<input type="submit" class="btn btn-primary" value="Pay Now">';
$htmlOutput .= '</form>';
return $htmlOutput;
}
<?php
function mygateway_capture(array $params): array
{
$merchantId = $params['merchantId'];
$secretKey = $params['secretKey'];
$invoiceId = $params['invoiceid'];
$amount = $params['amount'];
$currency = $params['currency'];
$cardNumber = $params['cardnum'];
$cardExpiry = $params['cardexp'];
$cardCvv = $params['cccvv'];
try {
$response = mygateway_apiCall('charge', [
'merchant_id' => $merchantId,
'amount' => $amount,
'currency' => $currency,
'card' => $cardNumber,
'expiry' => $cardExpiry,
'cvv' => $cardCvv,
'reference' => $invoiceId,
]);
logModuleCall('mygateway', 'capture', [
'amount' => $amount, 'invoice' => $invoiceId,
], $response, null, [$cardNumber, $cardCvv, $secretKey]);
if ($response['status'] === 'approved') {
return [
'status' => 'success',
'transid' => $response['transaction_id'],
'rawdata' => $response,
];
}
return [
'status' => 'declined',
'rawdata' => $response,
];
} catch (\Exception $e) {
logModuleCall('mygateway', 'capture', $params, $e->getMessage());
return [
'status' => 'error',
'rawdata' => $e->getMessage(),
];
}
}
<?php
// /modules/gateways/callback/mygateway.php
require_once __DIR__ . '/../../../init.php';
require_once __DIR__ . '/../../../includes/gatewayfunctions.php';
require_once __DIR__ . '/../../../includes/invoicefunctions.php';
$gatewayModuleName = basename(__FILE__, '.php');
$gatewayParams = getGatewayVariables($gatewayModuleName);
if (!$gatewayParams['type']) {
die("Module not activated");
}
// Read and validate the callback data
$transactionId = $_POST['transaction_id'] ?? '';
$invoiceId = $_POST['invoice_id'] ?? '';
$transactionAmount = $_POST['amount'] ?? '';
$paymentStatus = $_POST['status'] ?? '';
$signature = $_POST['signature'] ?? '';
// Verify the callback signature
$expectedSignature = hash_hmac(
'sha256',
$transactionId . $invoiceId . $transactionAmount,
$gatewayParams['secretKey']
);
if (!hash_equals($expectedSignature, $signature)) {
logTransaction($gatewayModuleName, $_POST, 'Invalid Signature');
die("Invalid signature");
}
// Validate invoice ID
$invoiceId = checkCbInvoiceID($invoiceId, $gatewayModuleName);
// Check for duplicate transaction
checkCbTransID($transactionId);
if ($paymentStatus === 'completed') {
// Add payment to the invoice
addInvoicePayment(
$invoiceId,
$transactionId,
$transactionAmount,
0, // Payment fee
$gatewayModuleName
);
logTransaction($gatewayModuleName, $_POST, 'Successful');
} else {
logTransaction($gatewayModuleName, $_POST, 'Payment Failed: ' . $paymentStatus);
}
| Location | Scope |
|-----------------------------------|--------------------------------|
| /includes/hooks/*.php | Global hooks (always active) |
| /modules/addons/{name}/hooks.php| Module hooks (when activated) |
| /modules/servers/{name}/hooks.php| Server module hooks |
Important: If you add
hooks.phpto a module after it has been activated, you must deactivate and reactivate the module (or re-save module settings) for WHMCS to recognise the new hooks file.
<?php
// Using a closure (anonymous function)
add_hook('ClientAdd', 1, function (array $vars) {
$clientId = $vars['userid'];
$firstName = $vars['firstname'];
$lastName = $vars['lastname'];
$email = $vars['email'];
logActivity("New client registered: {$firstName} {$lastName} (#{$clientId})");
});
// Using a named function (recommended for complex hooks)
add_hook('InvoicePaid', 1, 'mymodule_hookInvoicePaid');
function mymodule_hookInvoicePaid(array $vars): void
{
$invoiceId = $vars['invoiceid'];
// Fetch invoice details via Internal API
$invoice = localAPI('GetInvoice', ['invoiceid' => $invoiceId]);
if ($invoice['result'] === 'success') {
logActivity("Invoice #{$invoiceId} paid. Total: {$invoice['total']}");
}
}
1 for most hooks (default/highest priority).10+ for hooks that should run after others.| Hook | Trigger |
|--------------------------|-------------------------------------|
| ClientAdd | A new client account is created |
| ClientEdit | Client profile is updated |
| ClientClose | Client account is closed |
| ClientDelete | Client account is deleted |
| ClientChangePassword | Client changes their password |
| Hook | Trigger |
|--------------------------|-------------------------------------|
| InvoiceCreated | A new invoice is created |
| InvoicePaid | An invoice is marked as paid |
| InvoiceRefunded | An invoice is refunded |
| InvoiceCancelled | An invoice is cancelled |
| AddInvoicePayment | A payment is applied to an invoice |
| Hook | Trigger |
|--------------------------|-------------------------------------|
| AfterModuleCreate | After a service is provisioned |
| AfterModuleSuspend | After a service is suspended |
| AfterModuleUnsuspend | After a service is unsuspended |
| AfterModuleTerminate | After a service is terminated |
| PreModuleCreate | Before provisioning (can abort) |
| Hook | Trigger |
|--------------------------|-------------------------------------|
| TicketOpen | A new ticket is submitted |
| TicketAdminReply | An admin replies to a ticket |
| TicketUserReply | A client replies to a ticket |
| TicketClose | A ticket is closed |
| TicketStatusChange | Ticket status changes |
| Hook | Trigger |
|--------------------------|-------------------------------------|
| AfterRegistrarRegistration | Domain registration completes |
| AfterRegistrarTransfer | Domain transfer completes |
| AfterRegistrarRenewal | Domain renewal completes |
Full Hook Index: developers.whmcs.com/hooks/hook-index/ Hook Reference Manual: developers.whmcs.com/hooks-reference/
Use localAPI() when making API calls from within the WHMCS runtime
(modules, hooks, or custom pages). No authentication required — the call
runs with admin-level privileges.
<?php
// Example: Get client details
$results = localAPI('GetClientsDetails', [
'clientid' => $clientId,
'stats' => true,
]);
if ($results['result'] === 'success') {
$clientName = $results['fullname'];
$email = $results['email'];
$status = $results['status'];
}
// Example: Add a credit
localAPI('AddCredit', [
'clientid' => $clientId,
'amount' => 10.00,
'description' => 'Welcome credit applied by module',
]);
// Example: Open a ticket
localAPI('OpenTicket', [
'clientid' => $clientId,
'deptid' => 1,
'subject' => 'Account Provisioned',
'message' => 'Your account has been provisioned successfully.',
'priority' => 'Medium',
]);
// Example: Send an email
localAPI('SendEmail', [
'messagename' => 'Service Welcome Email',
'id' => $serviceId,
]);
Important:
localAPI()does not requireinit.phpwhen called from within hooks or modules (it's already loaded). Only includeinit.phpwhen calling from standalone scripts.
Use WHMCS\Module\Guzzle or GuzzleHttp\Client (bundled with WHMCS) for
external HTTP requests.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
function mymodule_apiCall(string $endpoint, array $data): array
{
$client = new Client([
'base_uri' => 'https://api.example.com/v1/',
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]);
try {
$response = $client->post($endpoint, [
'json' => $data,
]);
$result = json_decode($response->getBody()->getContents(), true);
logModuleCall('mymodule', $endpoint, $data, $result);
return $result;
} catch (RequestException $e) {
$errorResponse = $e->hasResponse()
? $e->getResponse()->getBody()->getContents()
: $e->getMessage();
logModuleCall('mymodule', $endpoint, $data, $errorResponse);
throw new \RuntimeException('API call failed: ' . $errorResponse);
}
}
API Index: developers.whmcs.com/api/api-index/ API Reference: developers.whmcs.com/api-reference/
.tpl extension and reside within the module's
templates/ directory.{php} tag — it is removed in Smarty v4.{$variable} for output and {if}, {foreach} for logic.{$variable|escape:'html'} for user-generated content.Use WHMCS built-in Bootstrap and admin CSS classes for a native look:
{* templates/admin/dashboard.tpl *}
<div class="tab-content">
{* Stats Cards *}
<div class="row">
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total Clients</h3>
</div>
<div class="panel-body text-center">
<h1>{$totalClients}</h1>
</div>
</div>
</div>
</div>
{* Data Table *}
<table id="myModuleTable" class="datatable" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Client</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{foreach from=$items item=row}
<tr>
<td>{$row.id}</td>
<td>{$row.client_name|escape:'html'}</td>
<td>
<span class="label label-{if $row.status == 'Active'}success{else}danger{/if}">
{$row.status}
</span>
</td>
<td>
<a href="{$moduleLink}&action=view&id={$row.id}" class="btn btn-sm btn-default">
<i class="fas fa-eye"></i> View
</a>
</td>
</tr>
{foreachelse}
<tr>
<td colspan="4" class="text-center">No records found.</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{* templates/client/dashboard.tpl *}
<div class="row">
<div class="col-md-12">
<h3>{$LANG.module_dashboard_title}</h3>
{if $data|count > 0}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{$LANG.column_id}</th>
<th>{$LANG.column_name}</th>
<th>{$LANG.column_status}</th>
</tr>
</thead>
<tbody>
{foreach from=$data item=item}
<tr>
<td>{$item.id}</td>
<td>{$item.key|escape:'html'}</td>
<td>{$item.value|escape:'html'}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{else}
<div class="alert alert-info">
{$LANG.no_records_found}
</div>
{/if}
</div>
</div>
☑ WHMCS Access Guard defined("WHMCS") or die("Access Denied");
☑ Parameter Binding Use Capsule for all DB queries (auto-binding)
☑ Input Validation Validate/sanitise all $_POST / $_GET input
☑ CSRF Protection check_token in POST requests (admin area)
☑ Credential Security Use encrypt()/decrypt() for stored secrets
☑ API Key Scrubbing Pass secrets to $replaceVars in logModuleCall()
☑ Output Escaping Use htmlspecialchars() or Smarty |escape
☑ Error Masking Log detailed errors; show generic messages to users
☑ File Permissions 644 for files, 755 for directories
☑ No Direct DB Access Never use mysql_*/mysqli_* — always Capsule
☑ HTTPS Enforcement Use 'forcessl' => true in client area output
☑ Rate Limiting Implement API call rate limits where applicable
☑ Signature Validation Verify HMAC signatures in gateway callbacks
Records API interactions viewable in Configuration → System Logs → Module Log.
<?php
logModuleCall(
'mymodule', // Module name
'CreateAccount', // Action being performed
$requestData, // Input parameters (string or array)
$rawResponse, // Raw API response
$processedResponse, // Processed/decoded response (optional)
[ // Sensitive strings to scrub from logs
$apiPassword,
$apiKey,
$cardNumber,
]
);
Records an entry in the WHMCS System Activity Log (visible to admins).
<?php
logActivity("MyModule: Client #{$clientId} provisioned on server {$serverName}");
logActivity("MyModule Error: Failed to connect to API - " . $e->getMessage());
<?php
// Provisioning modules: return a string
// 'success' on success, error message string on failure
return 'success';
return 'Error: Could not connect to server';
// Registrar modules: return an array
return ['success' => true];
return ['error' => 'Domain not found at registrar'];
// Addon activate/deactivate: return status array
return ['status' => 'success', 'description' => 'Module activated.'];
return ['status' => 'error', 'description' => 'Table creation failed.'];
// Gateway capture: return status array
return ['status' => 'success', 'transid' => $txnId, 'rawdata' => $response];
return ['status' => 'declined', 'rawdata' => $response];
return ['status' => 'error', 'rawdata' => $errorMessage];
Use the _upgrade function to manage database schema changes between
versions. WHMCS calls this function automatically when the version in
_config() is higher than the previously-stored version.
<?php
function mymodule_upgrade(array $vars): void
{
$currentVersion = $vars['version'];
// Version 1.1.0: Add status column
if (version_compare($currentVersion, '1.1.0', '<')) {
try {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->string('status', 50)->default('pending')->after('value');
});
logActivity("MyModule: Upgraded to v1.1.0 — added status column.");
} catch (\Exception $e) {
logActivity("MyModule: Upgrade to v1.1.0 failed — " . $e->getMessage());
}
}
// Version 1.2.0: Add index and new table
if (version_compare($currentVersion, '1.2.0', '<')) {
try {
Capsule::schema()->table('mod_mymodule_data', function ($table) {
$table->index('status');
});
if (!Capsule::schema()->hasTable('mod_mymodule_audit')) {
Capsule::schema()->create('mod_mymodule_audit', function ($table) {
$table->increments('id');
$table->unsignedInteger('record_id');
$table->string('action', 100);
$table->text('details')->nullable();
$table->timestamp('created_at')->useCurrent();
});
}
logActivity("MyModule: Upgraded to v1.2.0 — added audit table.");
} catch (\Exception $e) {
logActivity("MyModule: Upgrade to v1.2.0 failed — " . $e->getMessage());
}
}
}
| Pitfall | Why It Breaks | Fix |
|------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|
| Using mysql_* / mysqli_* | Removed/deprecated; breaks on PHP 8+ | Use Capsule::table() |
| Using {php} in Smarty templates | Removed in Smarty v4 (WHMCS 9.x) | Use Smarty tags or pass data via vars |
| Dynamic properties in classes | Deprecated in PHP 8.2, error in 9.0 | Declare all properties explicitly |
| Missing defined("WHMCS") guard | Files become directly accessible via URL | Add guard as first line of every PHP file |
| Hardcoding file paths | Breaks across installations/environments | Use ROOTDIR and WHMCS constants |
| Modifying core files | Overwritten on WHMCS updates, causes corruption | Use Hooks and Modules instead |
| Returning instead of echoing in _output| Admin output function must echo, not return | Use echo in addon _output() |
| Echoing in _clientarea | Client area function must return an array | Return ['templatefile' => ..., 'vars' => ...] |
| Pitfall | Issue | Fix |
|------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|
| Not wrapping DB calls in try/catch | Unhandled exceptions crash the admin area | Always use try/catch with Capsule operations |
| Forgetting logModuleCall() | Impossible to debug API issues in production | Log every external API call |
| Not scrubbing sensitive data from logs | Passwords/keys visible in Module Log | Pass sensitive strings in $replaceVars |
| Raw SQL table names without tbl prefix | WHMCS core tables use tbl prefix (e.g., tblclients) | Use exact table names including prefix |
| Adding hooks.php after module activation | WHMCS won't recognise new hooks file | Deactivate and reactivate module |
| Using $_REQUEST instead of specific | Security risk; ambiguous data source | Use $_POST or $_GET explicitly |
| Forgetting language files | Module is not translatable; hard-coded strings | Create lang/english.php with all strings |
| Not using the _upgrade function | Schema changes not applied when users update the module | Use version_compare() in _upgrade() |
| Illuminate v7 patterns on WHMCS 9.x | Fatal errors from incompatible Eloquent methods | Test queries specifically against Illuminate v9 |
| Not validating callback signatures | Gateway callbacks can be forged | Always verify HMAC signatures |
| Practice | Benefit |
|------------------------------------------|------------------------------------------------------------------|
| Use WHMCS native Bootstrap classes | Module UI looks native and professional |
| Include comprehensive language files | Module is instantly translatable for international users |
| Implement TestConnection (provisioning)| Admin can verify server connectivity before using the module |
| Include breadcrumbs in client area | Better navigation UX for clients |
| Use _upgrade for all schema changes | Seamless version upgrades without manual SQL |
| Bundle a README.md with the module | Users know how to install and configure |
| Add an Admin Dashboard Widget | Key metrics visible at a glance |
| Support configurable options via _config | No code changes needed for different environments |
/modules/addons/mymodule/
├── mymodule.php # Main file: _config, _activate, _deactivate,
│ # _upgrade, _output, _clientarea
├── hooks.php # Action hooks (auto-loaded when module active)
├── lang/
│ └── english.php # Language strings
├── lib/
│ ├── ApiClient.php # External API client class
│ ├── Helper.php # Utility functions
│ └── DataManager.php # Database abstraction layer
├── templates/
│ ├── admin/
│ │ ├── dashboard.tpl # Admin dashboard view
│ │ ├── settings.tpl # Admin settings view
│ │ └── list.tpl # Admin data listing view
│ └── client/
│ └── dashboard.tpl # Client area dashboard view
├── README.md # Installation & usage guide
├── CHANGELOG.md # Version history
└── LICENSE # License file
/modules/servers/myserver/
├── myserver.php # Main file: all provisioning functions
├── hooks.php # Action hooks (optional)
├── lib/
│ ├── ApiClient.php # Server API client class
│ └── Helper.php # Utility functions
├── templates/
│ └── clientarea.tpl # Client area service output
├── README.md
└── CHANGELOG.md
/modules/registrars/myregistrar/
├── myregistrar.php # Main file: all registrar functions
├── hooks.php # Action hooks (optional)
├── lib/
│ ├── ApiClient.php # Registrar API client class
│ └── DomainHelper.php # Domain utility functions
├── README.md
└── CHANGELOG.md
/modules/gateways/
├── mygateway.php # Main gateway file (_config, _link or _capture)
└── callback/
└── mygateway.php # Callback handler for payment notifications
/modules/gateways/mygateway/ # (Optional) Library files
├── lib/
│ └── ApiClient.php
└── README.md
<?php
// FIRST LINE of every PHP file in a module
defined("WHMCS") or die("Access Denied");
<?php
// /modules/addons/mymodule/lang/english.php
$_ADDONLANG['module_title'] = 'My Module';
$_ADDONLANG['dashboard_title'] = 'Dashboard';
$_ADDONLANG['settings_title'] = 'Settings';
$_ADDONLANG['client_page_title'] = 'My Module';
$_ADDONLANG['no_records_found'] = 'No records found.';
$_ADDONLANG['column_id'] = 'ID';
$_ADDONLANG['column_name'] = 'Name';
$_ADDONLANG['column_status'] = 'Status';
$_ADDONLANG['btn_save'] = 'Save Changes';
$_ADDONLANG['btn_cancel'] = 'Cancel';
$_ADDONLANG['success_saved'] = 'Settings saved successfully.';
$_ADDONLANG['error_generic'] = 'An error occurred. Please try again.';
<?php
use Illuminate\Database\Capsule\Manager as Capsule;
if (Capsule::schema()->hasTable('mod_mymodule_data')) {
// Table exists
}
if (Capsule::schema()->hasColumn('mod_mymodule_data', 'status')) {
// Column exists
}
<?php
// Inside _output() — get current admin user ID
$adminId = $_SESSION['adminid'];
$admin = Capsule::table('tbladmins')->where('id', $adminId)->first();
<?php
// Client area modules are accessed at:
// https://yourdomain.com/index.php?m=mymodule
// https://yourdomain.com/index.php?m=mymodule&action=settings
function mymodule_clientarea($vars): array
{
$action = $_GET['action'] ?? 'index';
switch ($action) {
case 'settings':
return [
'pagetitle' => 'Settings',
'templatefile' => 'templates/client/settings',
'requirelogin' => true,
'vars' => ['currentSettings' => $settings],
];
default:
return [
'pagetitle' => 'Dashboard',
'templatefile' => 'templates/client/dashboard',
'requirelogin' => true,
'vars' => ['data' => $data],
];
}
}
<?php
add_hook('ClientAreaPrimarySidebar', 1, function ($sidebar) {
/** @var \WHMCS\View\Menu\Item $sidebar */
$myPanel = $sidebar->addChild('myModulePanel', [
'label' => 'My Module',
'uri' => 'index.php?m=mymodule',
'order' => 99,
'icon' => 'fas fa-cog',
]);
$myPanel->addChild('dashboard', [
'label' => 'Dashboard',
'uri' => 'index.php?m=mymodule',
'order' => 1,
]);
$myPanel->addChild('settings', [
'label' => 'Settings',
'uri' => 'index.php?m=mymodule&action=settings',
'order' => 2,
]);
});
<?php
add_hook('ShoppingCartValidateCheckout', 1, function (array $vars) {
$errors = [];
// Example: require a custom field to be filled
if (empty($_POST['customfield_company'])) {
$errors[] = 'Company name is required for checkout.';
}
// Return errors to block checkout, or empty array to proceed
return $errors;
});
<?php
// Encrypt sensitive data before storing
$encrypted = encrypt('my-secret-api-key');
Capsule::table('mod_mymodule_settings')
->insert(['key' => 'api_key', 'value' => $encrypted]);
// Decrypt when reading
$row = Capsule::table('mod_mymodule_settings')
->where('key', '=', 'api_key')
->first();
$apiKey = decrypt($row->value);
| Resource | URL | |-------------------------------------|--------------------------------------------------------------| | Developer Documentation (Home) | https://developers.whmcs.com/ | | API Reference | https://developers.whmcs.com/api-reference/ | | API Index | https://developers.whmcs.com/api/api-index/ | | Hook Reference | https://developers.whmcs.com/hooks-reference/ | | Hook Index | https://developers.whmcs.com/hooks/hook-index/ | | Classes Reference | https://docs.whmcs.com/classes/ | | Style Guide | https://developers.whmcs.com/modules/style-guide/ | | Sample Addon Module | https://github.com/WHMCS/sample-addon-module | | Sample Provisioning Module | https://github.com/WHMCS/sample-provisioning-module | | Sample Registrar Module | https://github.com/WHMCS/sample-registrar-module | | Sample Gateway Module | https://github.com/WHMCS/sample-gateway-module | | Sample Merchant Gateway | https://github.com/WHMCS/sample-merchant-gateway | | WHMCS Community Forums | https://whmcs.community/ | | Beta API Documentation | https://api-beta.developers.whmcs.com/ |
This skill is licensed under the GNU General Public License v2.0. You may use, modify, and redistribute it under the terms of GPL-2.0.
Waqas Ahmed Waseer
whmcs-dev-skillsBuilt with ❤️ for the WHMCS developer community. Researched from official WHMCS Developer Documentation, common issue reports, and community best practices (2024–2025).
Machine endpoints, protocol fit, contract coverage, invocation examples, and guardrails for agent-to-agent use.
Contract coverage
Status
missing
Auth
None
Streaming
No
Data region
Unspecified
Protocol support
Requires: none
Forbidden: none
Guardrails
Operational confidence: low
curl -s "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/snapshot"
curl -s "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/contract"
curl -s "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/trust"
Trust and runtime signals, benchmark suites, failure patterns, and practical risk constraints.
Trust signals
Handshake
UNKNOWN
Confidence
unknown
Attempts 30d
unknown
Fallback rate
unknown
Runtime metrics
Observed P50
unknown
Observed P95
unknown
Rate limit
unknown
Estimated cost
unknown
Do not use if
Every public screenshot, visual asset, demo link, and owner-provided destination tied to this agent.
Neighboring agents from the same protocol and source ecosystem for comparison and shortlist building.
AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents
AI productivity studio with smart chat, autonomous agents, and 300+ assistants.
Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!
The Frontend for Agents & Generative UI. React + Angular
Contract JSON
{
"contractStatus": "missing",
"authModes": [],
"requires": [],
"forbidden": [],
"supportsMcp": false,
"supportsA2a": false,
"supportsStreaming": false,
"inputSchemaRef": null,
"outputSchemaRef": null,
"dataRegion": null,
"contractUpdatedAt": null,
"sourceUpdatedAt": null,
"freshnessSeconds": null
}Invocation Guide
{
"preferredApi": {
"snapshotUrl": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/snapshot",
"contractUrl": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/contract",
"trustUrl": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/trust"
},
"curlExamples": [
"curl -s \"https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/snapshot\"",
"curl -s \"https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/contract\"",
"curl -s \"https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/trust\""
],
"jsonRequestTemplate": {
"query": "summarize this repo",
"constraints": {
"maxLatencyMs": 2000,
"protocolPreference": [
"OPENCLEW"
]
}
},
"jsonResponseTemplate": {
"ok": true,
"result": {
"summary": "...",
"confidence": 0.9
},
"meta": {
"source": "GITHUB_OPENCLEW",
"generatedAt": "2026-10-09T11:19:25.949Z"
}
},
"retryPolicy": {
"maxAttempts": 3,
"backoffMs": [
500,
1500,
3500
],
"retryableConditions": [
"HTTP_429",
"HTTP_503",
"NETWORK_TIMEOUT"
]
}
}Trust JSON
{
"status": "unavailable",
"handshakeStatus": "UNKNOWN",
"verificationFreshnessHours": null,
"reputationScore": null,
"p95LatencyMs": null,
"successRate30d": null,
"fallbackRate": null,
"attempts30d": null,
"trustUpdatedAt": null,
"trustConfidence": "unknown",
"sourceUpdatedAt": null,
"freshnessSeconds": null
}Capability Matrix
{
"rows": [
{
"key": "OPENCLEW",
"type": "protocol",
"support": "unknown",
"confidenceSource": "profile",
"notes": "Listed on profile"
},
{
"key": "abort",
"type": "capability",
"support": "supported",
"confidenceSource": "profile",
"notes": "Declared in agent profile metadata"
},
{
"key": "be",
"type": "capability",
"support": "supported",
"confidenceSource": "profile",
"notes": "Declared in agent profile metadata"
},
{
"key": "verify",
"type": "capability",
"support": "supported",
"confidenceSource": "profile",
"notes": "Declared in agent profile metadata"
},
{
"key": "tickets",
"type": "capability",
"support": "supported",
"confidenceSource": "profile",
"notes": "Declared in agent profile metadata"
},
{
"key": "configurable",
"type": "capability",
"support": "supported",
"confidenceSource": "profile",
"notes": "Declared in agent profile metadata"
}
],
"flattenedTokens": "protocol:OPENCLEW|unknown|profile capability:abort|supported|profile capability:be|supported|profile capability:verify|supported|profile capability:tickets|supported|profile capability:configurable|supported|profile"
}Facts JSON
[
{
"factKey": "docs_crawl",
"label": "Crawlable docs",
"value": "6 indexed pages on the official domain",
"category": "integration",
"href": "https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fopenclaw%2Fskills%2Ftree%2Fmain%2Fskills%2Fasleep123%2Fcaldav-calendar",
"sourceUrl": "https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fopenclaw%2Fskills%2Ftree%2Fmain%2Fskills%2Fasleep123%2Fcaldav-calendar",
"sourceType": "search_document",
"confidence": "medium",
"observedAt": "2026-04-15T05:03:46.393Z",
"isPublic": true,
"metadata": {}
},
{
"factKey": "vendor",
"label": "Vendor",
"value": "Therabbirifat",
"category": "vendor",
"href": "https://github.com/TheRabbiRifat/whmcs-dev-skills",
"sourceUrl": "https://github.com/TheRabbiRifat/whmcs-dev-skills",
"sourceType": "profile",
"confidence": "medium",
"observedAt": "2026-04-15T02:13:28.380Z",
"isPublic": true,
"metadata": {}
},
{
"factKey": "protocols",
"label": "Protocol compatibility",
"value": "OpenClaw",
"category": "compatibility",
"href": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/contract",
"sourceUrl": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/contract",
"sourceType": "contract",
"confidence": "medium",
"observedAt": "2026-04-15T02:13:28.380Z",
"isPublic": true,
"metadata": {}
},
{
"factKey": "handshake_status",
"label": "Handshake status",
"value": "UNKNOWN",
"category": "security",
"href": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/trust",
"sourceUrl": "https://www.xpersona.co/api/v1/agents/therabbirifat-whmcs-dev-skills/trust",
"sourceType": "trust",
"confidence": "medium",
"observedAt": null,
"isPublic": true,
"metadata": {}
}
]Change Events JSON
[
{
"eventType": "docs_update",
"title": "Docs refreshed: Sign in to GitHub · GitHub",
"description": "Fresh crawlable documentation was indexed for the official domain.",
"href": "https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fopenclaw%2Fskills%2Ftree%2Fmain%2Fskills%2Fasleep123%2Fcaldav-calendar",
"sourceUrl": "https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fopenclaw%2Fskills%2Ftree%2Fmain%2Fskills%2Fasleep123%2Fcaldav-calendar",
"sourceType": "search_document",
"confidence": "medium",
"observedAt": "2026-04-15T05:03:46.393Z",
"isPublic": true,
"metadata": {}
}
]Sponsored
Ads related to whmcs-dev-skills and adjacent AI workflows.