WHMCS hooks: customizing without modifying core

Hooks are WHMCS's extensibility system. They let you run custom PHP code at specific events — client signup, invoice creation, ticket reply, etc. — without modifying WHMCS's core files. This article covers the basic hook patterns that solve common reseller customization needs.

Why use hooks

  • Survives upgrades. WHMCS upgrades replace core files. Hooks live in your includes/hooks/ directory, untouched.
  • Compose with other extensions. Multiple hooks can fire at the same event without stepping on each other.
  • Standard pattern. Anyone hiring a developer to customize WHMCS for you will write hooks.

Hook structure

A hook is a PHP file in includes/hooks/ that registers a function with WHMCS's event system:

<?php
// File: includes/hooks/my_first_hook.php
add_hook('ClientAdd', 1, function($vars) {
    // $vars contains the new client's data
    // Do something with it
    return [];
});

That's it. Drop the file in includes/hooks/, hit Save in your code editor, and the hook is active. WHMCS auto-discovers hooks at the path on each request.

Anatomy of a hook call

  • Event name (e.g., ClientAdd) — when WHMCS fires the hook
  • Priority (integer; lower runs first; default 1)
  • Callback function — receives $vars array containing context
  • Return value — usually empty array; some hook types let you modify behavior by returning specific structures

Common hook events

WHMCS has 200+ hook events. The most useful for a typical reseller:

Client lifecycle

  • ClientAdd — client signs up
  • ClientEdit — client info updated
  • ClientLogin — client logs in
  • ClientClose — client account closed

Service lifecycle

  • AfterModuleCreate — cPanel account just created on your server
  • AfterModuleSuspend — account suspended
  • AfterModuleUnsuspend — account unsuspended
  • AfterModuleTerminate — account terminated

Billing

  • InvoiceCreated — new invoice generated
  • InvoicePaid — invoice marked as paid
  • InvoiceCancelled — invoice cancelled

Support

  • TicketOpen — new ticket submitted
  • TicketAdminReply — admin posts a reply
  • TicketUserReply — client posts a reply

Cron / automation

  • DailyCronJob — runs once per day
  • AfterCronJob — runs after every 5-minute cron

Useful hook examples

Send a Slack notification on new signup

<?php
add_hook('ClientAdd', 1, function($vars) {
    $payload = json_encode([
        'text' => "New signup: {$vars['firstname']} {$vars['lastname']} ({$vars['email']})"
    ]);
    $ch = curl_init('https://hooks.slack.com/services/YOUR/WEBHOOK/URL');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_exec($ch);
    curl_close($ch);
    return [];
});

Email yourself when a high-value invoice is paid

<?php
add_hook('InvoicePaid', 1, function($vars) {
    if ($vars['amount'] > 100) {
        mail('you@yourdomain.com',
             "Big invoice paid: ${$vars['amount']}",
             "Invoice ID: {$vars['invoiceid']}
Client: {$vars['userid']}");
    }
    return [];
});

Add a custom welcome message to all new accounts

<?php
add_hook('AfterModuleCreate', 1, function($vars) {
    $client_email = WHMCSDatabaseCapsule::table('tblclients')
        ->where('id', $vars['userid'])
        ->value('email');

    mail($client_email,
         'Welcome to YourBrand',
         'Thanks for choosing us! Here are some getting-started resources...');
    return [];
});

Auto-tag tickets containing the word "urgent"

<?php
add_hook('TicketOpen', 1, function($vars) {
    if (stripos($vars['message'], 'urgent') !== false) {
        WHMCSDatabaseCapsule::table('tbltickets')
            ->where('id', $vars['ticketid'])
            ->update(['priority' => 'High']);
    }
    return [];
});

Database access in hooks

WHMCS exposes the Laravel Capsule database layer:

// SELECT
$client = WHMCSDatabaseCapsule::table('tblclients')->where('id', $vars['userid'])->first();

// UPDATE
WHMCSDatabaseCapsule::table('tblclients')->where('id', $vars['userid'])->update(['notes' => 'New note']);

// INSERT
WHMCSDatabaseCapsule::table('tbl_my_custom_table')->insert([
    'field1' => 'value1',
    'created' => date('Y-m-d H:i:s'),
]);

Avoid raw mysql_ functions or $query = "SELECT..." string-building — you'll create SQL injection vulnerabilities and your hook will break on WHMCS upgrades.

Logging from hooks

Don't use echo or var_dump — they break the page rendering. Instead:

logActivity("My hook did something interesting: " . print_r($vars, true), 0);

Logs appear in WHMCS Admin → Utilities → Logs → Activity Log.

Security considerations

  • Never trust hook input directly. Validate and escape any data you pass to mail(), database queries, or external APIs.
  • Use prepared statements. Capsule handles this automatically when you use the query builder.
  • Don't expose sensitive data. Logging full client objects exposes PII in logs. Filter what you log.
  • Use HTTPS for outbound webhooks. Slack, Discord, etc. all require HTTPS — make sure your curl calls use it.

Hook discovery and debugging

  • WHMCS Admin → Utilities → Logs → Module Log shows hook execution and errors.
  • Hook errors silently fail by default. Wrap your hook in try/catch and log exceptions.
  • The full hook reference: developers.whmcs.com/hooks

What to put in hooks vs. not

Good fits:

  • Triggering external APIs (Slack, Discord, custom CRM)
  • Custom email notifications beyond WHMCS templates
  • Auto-tagging or auto-categorizing tickets
  • Custom analytics events
  • Provisioning auxiliary services (DNS records, monitoring entries)

Not good fits:

  • Modifying WHMCS's built-in workflows (use module hooks or custom modules instead)
  • Heavy database operations (run them in a cron, not inline in user-facing requests)
  • Anything time-sensitive that should fail visibly — hooks are silent on failure

Common pitfalls

  • Hook file outside includes/hooks/. WHMCS auto-discovers hooks only in that directory.
  • Wrong PHP version. Hooks must work with the PHP version your WHMCS is running on. Test locally before deploying.
  • Slow external APIs blocking page loads. A signup flow that calls Slack synchronously will appear slow. Use curl_multi_* or background jobs for slow external calls.
  • Forgotten hooks after disabling a feature. If you wrote a hook for a feature you no longer use, remove the file. Orphaned hooks add complexity for the next person.
  • WHMCS, hooks, PHP, reseller, customization
  • 0 Los Usuarios han Encontrado Esto Útil
¿Fue útil la respuesta?

Artículos Relacionados

Setting up private nameservers for white-label reseller hosting

If you're running a white-label reseller hosting business on ipxcore, your clients should never...

Creating hosting packages in WHM

A "package" in WHM is a template that defines what every cPanel account created from it can do:...

Resource limits and overselling: avoiding disasters as a reseller

"Overselling" gets thrown around as a dirty word in hosting, but every reseller does it to some...

Migrating accounts between WHM servers

Whether you're moving a single client from your old reseller plan to your new ipxcore one,...

Onboarding your first reseller client: end-to-end workflow

Selling your first reseller hosting account is exciting. Onboarding it badly is how new resellers...