Custom Fields & API Integration
You can attach an unlimited number of custom fields to individual HireHop records (Jobs, Projects, Assets, Service Tests, etc.). These custom fields seamlessly merge into document templates. If a custom field exists on a record, it populates automatically; otherwise, it resolves as blank.
Currently, custom fields are fully supported on Jobs and Projects using custom JavaScript plugins.
Custom Fields Data Structure
When fetching custom field values for an active record, call the _get_custom_field_value(field) function. It returns NULL if unset, a plain string, or a structured JavaScript object.
For maximum printing control in documents, store custom fields as a structured JSON object rather than a plain string:
"field_name" :
{
"value" : "The value of the field",
"type" : "The field type, default is text, it can also be number, currency, text, date, html and array"
"format" : "For date type only, eg "ddd, dddddd tt" // = "Mon, 1 January 2017 12:00"
}
Object Keys & Supported Formats
| Parameter | Type | Description |
| value | Any | The raw field data stored in any valid JavaScript format. |
| type | String | Tells HireHop how to render the data in documents. Supported types: text (default), number, currency, date, html, and array (rendered as JSON). |
| format | String | Date types only. Specifies date/time output formatting based on user locale. |
Date Formatting Rules
Date values must be stored in yyyy-mm-dd hh:mm format. You can combine the following tokens and custom separators:
dddddd– Long date (e.g., 1 January 2026)ddddd– Short date (e.g., 01/01/2026)dddd– Full day of the week (e.g., Monday)ddd– Short day of the week (e.g., Mon)tt– Time (e.g., 12:36 am)
Example: dddd, dddddd tt outputs “Monday, January 1 2026 12:00”.
Saving Custom Fields
To store updated custom field values on supported edit forms, use _save_custom_field_value(field, value).
- Batch Operations: All changes must be written prior to submitting the form save action. New values merge with existing fields; matching key names will overwrite existing values.
- Direct API Calls: When calling endpoints like
/php_functions.job_save.phpdirectly, passing only thecustom_fieldsPOST parameter updates the custom fields while leaving all other record attributes untouched.
Naming & Printing Conventions
- Document Merge Syntax: In document templates, prefix field names with a single underscore
_. For example, a custom field namedfield_nameon a Job merges asjob:_field_name.
- Namespace Prefixes: To prevent naming collisions across different plugins or record types (e.g., shared asset and test fields), prefix field names with a unique identifier (e.g., HireHop plugins use
hh_Field Name).
- Case Sensitivity: Document merge tags are case-insensitive, but JavaScript variable references are case-sensitive.
Searchable Custom Fields
The CUSTOM_INDEX field is a special 45-character string value (or NULL) reserved for custom searching, filtering, and home page search results.
Enabling Home Page Search
To include CUSTOM_INDEX in global search columns, append it to allSearchCols and set the label in your plugin:
// Add CUSTOM_INDEX to search columns
if (allSearchCols.constructor === Array && doc_type == 0) {
allSearchCols.push("CUSTOM_INDEX");
}
// Define display label if not set in language settings
if (typeof(lang["customIndexTxt"]) == "undefined" || lang["customIndexTxt"] == "") {
lang["customIndexTxt"] = "Custom Field Name";
}
Displaying & Interacting with CUSTOM_INDEX
On Job pages, unhide the tile element to hook custom interactions:
$("#job_tile_custom_index")
.show()
.click(function() {
window.open("https://www.my_external_app.com?id=" + job_data["CUSTOM_INDEX"], "newwindow");
});
Adding CUSTOM_INDEX to Edit Widgets
To allow users to input CUSTOM_INDEX values inside the Job Edit dialog, inject a form row into the widget:
if (typeof($.custom.job_edit) != "undefined") {
$.widget("custom.job_edit", $.custom.job_edit, {
_init_main: function() {
this._super(arguments);
// Inject custom field row into the edit table
var table = this.default_disc.closest("table");
var tr = $("<tr>").appendTo(table);
$("<td>", { html: lang.customIndexTxt + " :" }).appendTo(tr);
$("<input>", {
"name": "custom_index",
"class": "data_cell",
"data-field": "CUSTOM_INDEX",
"maxlength": 45
}).appendTo($("<td>").appendTo(tr));
// Adjust layout spacing
this.job_edit_memo.height(110);
}
});
}
Note: In documents, reference this field as xxx:custom_index.
Global Custom Fields
To store company-wide global settings or counters, use the global custom fields endpoints (/php_functions/custom_fields_global_load.php and /php_functions/custom_fields_global_save.php):
$("#saving_dialog").dialog("open");
$.ajax({
url: "/php_functions/custom_fields_global_save.php",
type: "post",
dataType: "json",
data: {
"fields": { "my_field": "any type of value" }
},
success: function(data) {
$("#saving_dialog").dialog("close");
if (typeof(data.error) !== "undefined") {
error_message(isNaN(parseInt(data.error)) ? data.error : lang.error[data.error]);
} else {
// Returns JSON object containing all global custom fields
}
},
error: function(jqXHR, textStatus, errorThrown) {
$("#saving_dialog").dialog("close");
error_message(lang.error[1] + " (" + errorThrown + ").");
}
});
Settings vs. API Custom Fields
Fields created in Settings > Custom Fields share the exact same underlying database storage as API-injected custom fields. Setting up a field in the UI merely provides a codeless builder; deleting a UI-created custom field does not delete historical field data, which remains accessible via the API.