Skip to main content

HeskAPI Documentation

REST API for HESK 3.7.12 · PHP 8.1+

Quick Start

Upload the drop-in zip

Extract the HeskAPI zip into your HESK root directory (same folder as hesk_settings.inc.php). The module adds an api/ directory and an admin/ panel page — no HESK files are modified.

Your license is already in place

Your download includes a ready-to-use heskapi_license.php, pre-filled and locked to your domain — just keep it in the HESK root alongside the other files. The API validates this license against your domain on startup, so don't edit or move it. (Bought before this was automatic? Re-download from your activation email, or paste the key we sent into a heskapi_license.php in the HESK root.)

Make your first API call

Send a request to /api/v1/?action=list_tickets with your API key. You should receive a JSON response with your HESK tickets.

Authentication

Every request to the HeskAPI requires an API key. Generate and manage keys from the HeskAPI admin panel inside HESK (Admin → HeskAPI Manager).

Pass the key via HTTP header (recommended):

X-HESK-API-Key: your_api_key_here

Or via query parameter:

GET /api/v1/?action=list_tickets&key=your_api_key_here

API keys can be rotated at any time from the admin panel without downtime.

Rate Limiting

HeskAPI enforces configurable per-key rate limits using atomic database counters.

HeaderDescription
X-RateLimit-LimitMax requests per minute for this key
X-RateLimit-RemainingRequests remaining in current window
Retry-AfterSeconds until the rate limit resets (only on 429)

When the limit is exceeded the API returns HTTP 429 with a JSON error body. Configure the limit per API key from the admin panel.

Endpoints

GET /api/v1/?action=list_tickets

Returns a paginated list of HESK tickets.

ParameterTypeDescription
pageintPage number (default: 1)
per_pageintResults per page (default: 25, max: 100)
statusintFilter by status: 0=open, 1=resolved, 2=in progress
categoryintFilter by HESK category ID
GET /api/v1/?action=search_tickets

Full-text search across ticket subjects and messages.

ParameterTypeDescription
qstringSearch query (required)
pageintPage number
per_pageintResults per page
POST /api/v1/?action=create_ticket

Creates a new HESK ticket. Supports multipart/form-data for file attachments and triggers native HESK email notifications. When email verification is enabled (see below), the ticket is held and this endpoint returns HTTP 202 instead of creating it immediately.

FieldTypeRequiredDescription
namestringYesCustomer name
emailstringYesCustomer email
subjectstringYesTicket subject
messagestringYesTicket message body
categoryintNoHESK category ID (default: 1)
priorityintNo0=Critical, 1=High, 2=Medium, 3=Low
client_ipstringNoReal end-user IP of the submitter (e.g. from a contact form behind Cloudflare). When passed, it is stored on the ticket and checked against HESK's banned-IP list instead of the calling server's IP. Falls back to the caller IP if omitted.
attachmentfileNoFile attachment (multipart only). Ignored while a submission is pending email verification.
Email verification (double opt-in). When Require verification is enabled under Admin › HeskAPI Manager › Spam Protection, create_ticket does not create the ticket right away — it stores the submission, emails the sender a single-use confirmation link, and returns:
HTTP 202 Accepted
{"success":true,"data":{"status":"pending_verification","email":"[email protected]"}}
The ticket is created only when the sender clicks the link (see verify_ticket). Links expire after 72 hours, confirmation emails are rate-limited per address (excess returns HTTP 429), and attachments are not accepted until the request is confirmed. Modes: Off, All tickets, or Unknown emails only (addresses already known to the desk skip verification).
Verification method — link or code. The default emails a confirmation link. Alternatively, set the method to 6-digit code: create_ticket then returns a confirmation_id and emails a numeric code, which you submit to confirm_ticket to create the ticket — ideal when your integration keeps the user on your own form:
HTTP 202 Accepted
{"success":true,"data":{"status":"pending_verification","method":"code","confirmation_id":"a1b2…","expires_in_hours":72}}
POST /api/v1/?action=update_ticket

Updates fields on an existing ticket (status, priority, category, or custom fields).

FieldTypeRequiredDescription
trackidstringYesTicket track ID (e.g. ABC-123456)
statusintNoNew status: 0=open, 1=resolved, 2=in progress
priorityintNoNew priority (0–3)
categoryintNoNew category ID
POST /api/v1/?action=reply_ticket

Posts a staff reply to a ticket. Triggers native HESK customer email notification.

FieldTypeRequiredDescription
trackidstringYesTicket track ID
messagestringYesReply body
attachmentfileNoFile attachment (multipart)
POST /api/v1/?action=close_ticket

Marks a ticket as resolved.

FieldTypeRequiredDescription
trackidstringYesTicket track ID
GET /api/v1/?action=verify_ticket&token=…

The browser-facing confirmation link emailed to a submitter when email verification is enabled. No API key — the single-use token is the credential. Clicking it creates the held ticket, then shows a confirmation page (or redirects to the admin-configured success URL). Expired or already-used links show a friendly message. You never call this directly; it is the link embedded in the confirmation email.

FieldTypeRequiredDescription
tokenstringYesSingle-use confirmation token from the email (expires after 72h)
POST /api/v1/?action=confirm_ticket

Confirms a submission created under the 6-digit code verification method and creates the held ticket. API key required. Submit the confirmation_id returned by create_ticket together with the code the sender received by email; on success it returns the created ticket exactly like create_ticket. The code is single-use and attempts are capped (the row is discarded after too many wrong tries).

FieldTypeRequiredDescription
confirmation_idstringYesThe handle returned by create_ticket in code mode
codestringYesThe 6-digit code from the confirmation email

Error Codes

HTTP StatusError CodeDescription
401invalid_keyMissing or invalid API key
403ip_bannedRequesting IP is in HESK's ban list
403email_bannedEmail address is in HESK's ban list
403api_disabledAPI is disabled in the admin panel
404ticket_not_foundThe specified track ID does not exist
422validation_errorMissing or invalid request parameter
429rate_limitRate limit exceeded — see Retry-After header
500server_errorInternal server error

All error responses include a JSON body: {"status":"error","code":"...","message":"..."}

Code Examples

PHP — List tickets
$ch = curl_init('https://yoursite.com/hesk/api/v1/?action=list_tickets');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['X-HESK-API-Key: your_api_key_here'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PHP — Create ticket
$ch = curl_init('https://yoursite.com/hesk/api/v1/?action=create_ticket');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => [
        'name'    => 'Jane Smith',
        'email'   => '[email protected]',
        'subject' => 'Login issue',
        'message' => 'I cannot log in since yesterday.',
        'category' => 1, // required: target HESK category ID
        'client_ip' => $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'], // real end-user IP
    ],
    CURLOPT_HTTPHEADER => ['X-HESK-API-Key: your_api_key_here'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript (fetch) — List tickets
const res = await fetch('https://yoursite.com/hesk/api/v1/?action=list_tickets', {
    headers: { 'X-HESK-API-Key': 'your_api_key_here' },
});
const data = await res.json();