Tutorial How to Setup Standardized Hooks with BASH in cPanel & WHM

This guide is for cPanel users who have root access on their cPanel server and would like to add custom functionality to cPanel and/or WHM using the BASH scripting language.

It is important to keep in mind that this guide only covers the installation of a basic script. This is useful for simple functionality that needs to be implemented quickly and easily. You also have the option of writing a Perl module rather than writing a script. When writing a Perl module, your code is run within the cpsrvd deamon. That allows your perl module access to cPanel Environment variables, which are not available with scripts.

The Standardized Hooks system in cPanel allows the systems administrator or developer to register scripts with cPanel to be run at the selected hook event.

Using the basics outlined here, the systems administrator or developer can take advantage of the flexible and powerful Standardized Hooks system to bolt on many kinds of custom functionality.

The Basics
There are only 3 main steps required to setup a Standardized Hook Script in cPanel.
  1. Identify the Hookable Event that you would like your script to run on
  2. Write the script that you would like to use
  3. Register the script in cPanel so that it is associated with the hookable event
1. Identifying The Appropriate Hookable Event
A Hookable Event is an event that happens within the regular operation of cPanel and WHM. Examples are: An account is suspended, Backups run, An Account is created, etc.

When identifying the appropriate Hookable Event you will want to browse the available events and think about which one makes the most sense for the particular functionality that you are adding. For example, if you wanted to Automatically install WordPress for new accounts, you would likely choose the Post Account Creation event.

To find what Hookable Events are available check our Documentation About Hookable Events

2. Writing The Script
The main cPanel specific question that you'll have is how to access the data that is provided by a cPanel Hookable Event. Hookable Events in cPanel publish their data to STDIN. You can read this data in bash by doing the following:
tmpfile="$(mktemp -p /tmp custom-script-data-XXXXXXXX)"
cat "${1:-/dev/stdin}" > $tmpfile
This data is provided in JSON format. One easy way of obtaining values from the JSON data is to use Python. For example, you can obtain the username of a newly created account after the Create Account event with the following:
cpanelusername=$(python -c "import sys, json; print json.load(open('$tmpfile'))['data']['user']")

3. Registering Your Script and Managing Hooks
For brevity, I'll only provide examples below. It is important that you read the full documentation on managing hooks here in order to fully understand the examples below.

In order to register your executable with cPanel so that it is run every time the Hookable event occurs you'll use the manage_hooks utility:
/usr/local/cpanel/bin/manage_hooks add script /usr/local/cpanel/3rdparty/bin/ --manual --category Whostmgr --event Accounts::Create --stage=post
In order to list the existing hooks issue the following command:
/usr/local/cpanel/bin/manage_hooks list
In order to delete an existing hook you'll need to specify the script a long with a few other details:
/usr/local/cpanel/bin/manage_hooks delete script /usr/local/cpanel/3rdparty/bin/ --manual --category Whostmgr --event Accounts::Create --stage=post
Debugging Hook Action Scripts
You have three main methods for debugging when writing and testing your Standardized Hook Scripts in BASH:
  1. Within your script, direct output to a file. Here is an example:
    data="This string will be put into your log file"
    echo $data > /root/debugDataVariableContents.txt
    cat "${1:-/dev/stdin}" > /root/debugHookOutputData.txt
  2. Turn on "Standardized Hooks - Debug Mode" via Tweak Settings in WHM and then tail the log at /usr/local/cpanel/logs/error_log
  3. You may also write to the cPanel error log, which is covered in the section below entitled "Handling Errors"
If you have a script of any complexity, you'll likely use all three of these methods during the course of creating your script.

When you are trying to determine what kind of data is available to you after an event runs, you can use the first method as shown above to view that data. When you do this, you'll quickly find that the JSON data that is provided via STDIN is NOT formatted for human consumption. This can make it very difficult to review the output to debug and design your script properly.

Luckily there is a tool that makes reading JSON easy. Once you have saved some JSON output to a file using the first debug method, you can use the json.tool module in python to format the data. Here is an example of the JSON data generated upon account creation formatted by the json.tool python module:

[root@srv00001 ~]# python -m json.tool /root/debugHookOutputData.txt
    "context": {
        "category": "Whostmgr",
        "event": "Accounts::Create",
        "point": "main",
        "stage": "post"
    "data": {
        "bwlimit": 0,
        "contactemail": "",
        "cpmod": "paper_lantern",
        "digestauth": "n",
        "dkim": "1",
        "domain": "scripthook4.tld",
        "featurelist": "default",
        "force": null,
        "forcedns": 0,
        "gid": "",
        "hascgi": "y",
        "hasshell": "y",
        "homedir": "/home/scripthook4",
        "homeroot": "/home",
        "is_restore": 0,
        "locale": "en",
        "mailbox_format": null,
        "max_defer_fail_percentage": "unlimited",
        "max_email_per_hour": "500",
        "maxaddon": 0,
        "maxftp": "n",
        "maxlst": "n",
        "maxpark": 0,
        "maxpop": "n",
        "maxsql": "n",
        "maxsub": "n",
        "mxcheck": "local",
        "no_cache_update": 0,
        "owner": "root",
        "pass": "REDACTED",
        "plan": "default",
        "quota": "unlimited",
        "skip_mysql_dbowner_check": 0,
        "spf": "1",
        "uid": "",
        "useip": "n",
        "user": "scripthook4",
        "useregns": 0
    "hook": {
        "escalateprivs": 0,
        "exectype": "script",
        "hook": "/usr/local/cpanel/3rdparty/bin/",
        "id": "JNVpUXPPLXpR6mxcXW1d46ma",
        "stage": "post",
        "weight": 200
As you can see in the example above, we used the following command to format the
/root/debugHookOutputData.txt file into human readable output:
python -m json.tool /root/debugHookOutputData.txt
Handling Errors
cPanel automatically catches data sent to STDERR from a script that is executed as a script hook. cPanel then publishes this data to the cPanel error log at /usr/local/cpanel/logs/error_log .

Here is an example of what happens when mktemp is unable to write to a location that does not exist on the server:
[2018-09-26 13:06:48 +0000] info [whostmgr5] STDERR output from hook: /usr/local/cpanel/3rdparty/bin/
[2018-09-26 13:06:48 +0000] info [whostmgr5] mktemp: failed to create file via template `/fakedir/custom-tmp-file-XXXXXXXX': No such file or directory
You can also write your own custom error messages by redirecting output to STDERR from your script. Let's improve our above script that captures data from STDIN, by adding some error handling in case we are not able to write to the tmp file.
tmpfile="$(mktemp -p /tmp custom-script-data-XXXXXXXX)"
cat "${1:-/dev/stdin}" > $tmpfile
if [ $? -ne 0 ]; then
        echo "Custom script is unable to create a temporary file and has exited early." > /proc/self/fd/2
        exit 1
This is the result:
[2018-09-26 13:37:45 +0000] info [whostmgr5] STDERR output from hook: /usr/local/cpanel/3rdparty/bin/
[2018-09-26 13:37:45 +0000] info [whostmgr5] mktemp: failed to create file via template `/fakedir/custom-script-data-XXXXXXXX': No such file or directory
Custom script is unable to create a temporary file and has exited early.

[2018-09-26 13:37:45 +0000] info [whostmgr5] End STDERR from hook
I would like to point out that there are a number of ways that you can write to STDERR in BASH, which include, but is not limited to:
echo "Helpful error message here." > /dev/stderr
echo "Helpful error message here." 1>&2
>&2 echo "Helpful error message here."
echo "Helpful error message here." > /proc/self/fd/2
I'll provide some brief context to the option that I've chosen to use in this example.
/proc/self is a link to the current process. /proc/self/fd contains the open file descriptors, and 2 is the file descriptor for STDERR. /proc/self/fd/0 represents STDIN, and /proc/self/fd/1 represents STDOUT.

So when we redirect output to the /proc/self/fd/2 file descriptor, we are sending the output to STDERR, which is then caught by cPanel and published to the cPanel error_log.

Beyond The Basics
There is quite a lot more that you can do with Standardized Hooks in cPanel and WHM. This guide is a pared down introduction. I recommend reviewing our full documentation on the topic if you require more advanced functionality.

Admin Note:
This cPanel staff tutorial is no longer being maintained/updated here. For the most recent/up to date version please go here: How to Setup Standardized Hooks with BASH in cPanel & WHM
First release
Last update
0.00 star(s) 0 ratings