Extends osTicket's REST API with powerful endpoints for advanced ticket management. Enables ticket creation with Markdown formatting, department routing, subticket support, and comprehensive ticket management (update, retrieve, search, delete, statistics).
Perfect for integrations that need granular control beyond osTicket's standard API capabilities - ideal for support portals, bug trackers, automation platforms (Zapier/Make.com), and custom workflows.
| Requirement | Version | Notes |
|---|---|---|
| osTicket | 1.18.x | Plugin extends API via Signal system |
| PHP | 8.1+ | Uses modern PHP features: enums, union types, named arguments |
| Web Server | Apache 2.4+ or NGINX 1.18+ | Apache: .htaccess support required |
| Parsedown | Automatic | Included in Composer lock |
Optional Dependencies:
| Plugin | Purpose | Link |
|---|---|---|
| Markdown Support | Markdown rendering in tickets | GitHub |
| Subticket Manager | UI for subticket management | GitHub |
| API Key Wildcard | Wildcard IP validation for API keys | GitHub |
api-endpoints folder to /include/plugins/ on your osTicket serverFinal path: /path/to/osticket/include/plugins/api-endpoints/
cd /path/to/osticket/include/plugins
git clone https://github.com/markus-michalski/osticket-api-endpoints.git
The plugin automatically configures:
On activation, the plugin automatically deploys /api/.htaccess:
# osTicket API Endpoints Plugin - Apache Configuration
<IfModule mod_rewrite.c>
RewriteEngine On
# Enable PATH_INFO for all API endpoints
AcceptPathInfo On
# Rewrite rules for endpoints with path info
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(tickets-update|tickets-get|tickets-delete)\\.php/(.+)$ $1.php [L,QSA]
</IfModule>
# Allow PATH_INFO for endpoints
<Files "tickets-update.php">
AcceptPathInfo On
</Files>
<Files "tickets-get.php">
AcceptPathInfo On
</Files>
<Files "tickets-delete.php">
AcceptPathInfo On
</Files>
Add the following configuration to your NGINX server block:
# osTicket API Endpoints Plugin - NGINX Configuration
# Endpoints with PATH_INFO (e.g., /api/tickets-update.php/123456.json)
location ~ ^/api/tickets-(update|get|delete)\\.php/ {
fastcgi_split_path_info ^(/api/tickets-(update|get|delete)\\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
# Attachment download endpoint
location ~ ^/api/tickets-attachment-download\\.php/ {
fastcgi_split_path_info ^(/api/tickets-attachment-download\\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
# Endpoints without PATH_INFO (e.g., /api/tickets-search.php?query=test)
location ~ ^/api/tickets-(search|stats|statuses)\\.php$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
# Subticket endpoints with PATH_INFO
location ~ ^/api/tickets-subtickets-(parent|list)\\.php/ {
fastcgi_split_path_info ^(/api/tickets-subtickets-(parent|list)\\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
# Subticket endpoints without PATH_INFO
location ~ ^/api/tickets-subtickets-(create|unlink)\\.php$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
After plugin installation, navigate to Admin Panel → Manage → API Keys to configure permissions for the new endpoints.
After plugin installation, a new configuration page appears under Admin Panel → Plugins → API Endpoints → Configure, displaying all available endpoints with their associated permissions:
Available API Endpoints:
can_create_tickets permissioncan_read_tickets permissioncan_read_tickets permissioncan_update_tickets permissioncan_search_tickets permissioncan_delete_tickets permissioncan_read_stats permissioncan_manage_subtickets permissionThe plugin provides 12 REST endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/tickets.json |
POST | Extended ticket creation (Markdown, Department, Subticket, Attachments) |
/api/tickets-get.php/{number}.json |
GET | Retrieve ticket details (incl. attachment metadata) |
/api/tickets-update.php/{number}.json |
PATCH | Update ticket (Status, Department, SLA, dueDate, Attachments, etc.) |
/api/tickets-search.php |
GET | Search tickets |
/api/tickets-delete.php/{number}.json |
DELETE | Permanently delete ticket |
/api/tickets-attachment-download.php/{file_id}.json |
GET | Download ticket attachment (Base64) |
/api/tickets-stats.php |
GET | Ticket statistics (global, Department, Staff) |
/api/tickets-statuses.php |
GET | List available ticket statuses |
/api/tickets-subtickets-parent.php/{id}.json |
GET | Get parent ticket of a subticket |
/api/tickets-subtickets-list.php/{id}.json |
GET | Get all child tickets of a parent |
/api/tickets-subtickets-create.php |
POST | Create parent-child relationship |
/api/tickets-subtickets-unlink.php |
DELETE | Remove parent-child relationship |
All GET endpoints support both JSON and XML:
.json for JSON response.xml for XML responseUpdate ticket properties such as status, department, priority, SLA plan, staff assignment, and due date.
URL: PATCH /api/tickets-update.php/{number}.{format}
Updatable Fields:
| Field | Type | Description | Example |
|---|---|---|---|
statusId |
string/int | Status name or ID | "Resolved" or 3 |
departmentId |
string/int | Department name or ID | "Sales" or 7 |
topicId |
int | Help Topic ID | 15 |
slaId |
string/int | SLA Plan name or ID | "Premium Support" or 2 |
staffId |
string/int | Staff username or ID | "john.doe" or 5 |
teamId |
int | Team ID for assignment | 3 |
priority |
string/int | Priority name or ID | "Urgent", "High" or 4 |
dueDate |
string/null | Due date (ISO 8601) or null to clear |
"2026-01-31" or "2026-01-31T17:30:00" |
note |
string | Add internal note (staff only) | "Customer called for status update" |
attachments |
array | File attachments (data-URI format, requires note) |
See File Attachments |
null to remove the due dateisOverdue flag is automatically clearedExamples:
# Set due date
curl -X PATCH "https://osticket.local/api/tickets-update.php/123456.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dueDate": "2026-01-31"}'
# Clear due date
curl -X PATCH "https://osticket.local/api/tickets-update.php/123456.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dueDate": null}'
The API supports attaching files to tickets - both during creation and on updates (via internal notes).
Attachments are passed as a JSON array in Base64 data-URI format:
{
"attachments": [
{"screenshot.png": "data:image/png;base64,iVBORw0KGgo..."},
{"report.pdf": "data:application/pdf;base64,JVBERi0xLjQ..."}
]
}
| Limit | Value |
|---|---|
| Max files per request | 5 |
| Max file size per file | 10 MB |
| Max total size | 50 MB |
| Category | File Types |
|---|---|
| Images | PNG, JPG/JPEG, GIF, SVG, WebP, BMP, ICO |
| Documents | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, ODT, ODS |
| Text | TXT, MD, CSV, JSON, XML, YAML, LOG, HTML, CSS, JS |
| Archives | ZIP, TAR, GZ, 7Z, RAR |
FILE_B64=$(base64 -w 0 /path/to/screenshot.png)
curl -X POST "https://osticket.local/api/tickets.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"subject": "Bug Report with Screenshot",
"message": "See attached screenshot for error details.",
"format": "markdown",
"attachments": [
{"screenshot.png": "data:image/png;base64,'"$FILE_B64"'"}
]
}'
File attachments on updates require a
note. Without a note, attachments cannot be added to existing tickets.
FILE_B64=$(base64 -w 0 /path/to/audit-report.pdf)
curl -X PATCH "https://osticket.local/api/tickets-update.php/123456.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"note": "Security audit completed, report attached.",
"noteTitle": "Audit Result",
"attachments": [
{"SECURITY-AUDIT.pdf": "data:application/pdf;base64,'"$FILE_B64"'"}
]
}'
Tip: When using the MCP Server (Claude Code), files are automatically Base64-encoded. Simply provide the absolute file path.
Since version 2.3.0, ticket attachments can be downloaded via the API. The workflow consists of two steps: first retrieve the file_id from the attachment metadata in the thread entry, then download the content.
When retrieving a ticket (GET /tickets-get.php/{number}.json), thread entries automatically include attachment metadata if attachments are present:
{
"ticket": {
"thread": [
{
"id": 540,
"type": "N",
"poster": "API",
"body": { "body": "Security audit file attached.", "type": "markdown" },
"attachments": [
{
"id": 35,
"file_id": 28,
"filename": "SECURITY.md",
"size": 12882,
"mime_type": "text/markdown",
"inline": false
}
],
"attachment_count": 1
}
]
}
}
Attachment metadata fields:
| Field | Type | Description |
|---|---|---|
id |
int | Attachment ID (internal reference) |
file_id |
int | File ID - required for download |
filename |
string | File name |
size |
int | File size in bytes |
mime_type |
string | MIME type (e.g., application/pdf, image/png) |
inline |
boolean | true = embedded image in text, false = standalone attachment |
Thread entries without attachments do not contain
attachmentsandattachment_countfields. Check for existence before accessing them.
URL: GET /api/tickets-attachment-download.php/{file_id}.json
Permission: can_read_tickets
Parameters:
| Parameter | Type | Description |
|---|---|---|
file_id |
int | The file_id from the attachment metadata |
Response:
{
"file_id": 28,
"filename": "SECURITY.md",
"mime_type": "text/markdown",
"size": 12882,
"content": "IyBTZWN1cml0eSBBdWRpdCBSZXBvcnQK..."
}
| Field | Type | Description |
|---|---|---|
file_id |
int | File ID |
filename |
string | Original file name |
mime_type |
string | MIME type |
size |
int | File size in bytes |
content |
string | Base64-encoded file content |
Step 1 - Get file_id:
# Retrieve ticket and extract file_id from response
curl -s "https://osticket.local/api/tickets-get.php/123456.json" \
-H "X-API-Key: YOUR_API_KEY" \
| jq '.ticket.thread[].attachments[]? | {file_id, filename, size}'
Output:
{"file_id": 28, "filename": "SECURITY.md", "size": 12882}
{"file_id": 29, "filename": "screenshot.png", "size": 45312}
Step 2 - Download file:
# Download file and decode Base64
curl -s "https://osticket.local/api/tickets-attachment-download.php/28.json" \
-H "X-API-Key: YOUR_API_KEY" \
| jq -r '.content' \
| base64 -d > SECURITY.md
| HTTP Code | Cause | Example |
|---|---|---|
| 400 | Invalid file_id | {"error": "Invalid file ID"} |
| 403 | File not linked to a ticket attachment | {"error": "Attachment not accessible"} |
| 404 | File not found | {"error": "File not found"} |
Security: The download endpoint verifies that the
file_idactually belongs to a ticket attachment. Arbitrary file IDs from theost_filetable (e.g., logos, backdrops) are rejected with HTTP 403.
The plugin extends osTicket's API key permission system with granular permissions.
| Permission | Database Field | Grants Access To |
|---|---|---|
| Create Tickets | can_create_tickets |
POST /tickets |
| Read Tickets | can_read_tickets |
GET /tickets/:number, GET /tickets-attachment-download/:file_id |
| Update Tickets | can_update_tickets |
PATCH /tickets/:number |
| Search Tickets | can_search_tickets |
GET /tickets/search |
| Delete Tickets | can_delete_tickets |
DELETE /tickets/:number |
| Read Statistics | can_read_stats |
GET /tickets-stats, GET /tickets-statuses |
| Manage Subtickets | can_manage_subtickets |
All subticket endpoints |
The plugin supports legacy API keys (without new permissions):
can_create_tickets missing → fallback to canCreateTickets() (osTicket standard)can_read_tickets missing → fallback to canCreateTickets() (READ = CREATE allowed)can_update_tickets missing → fallback to canCreateTickets()Principle of Least Privilege:
can_read_statscan_create_ticketsRotation & Revocation:
Audit Logging:
Symptoms:
curl "https://osticket.local/api/tickets-get.php/123456.json"
# HTTP 404 Not Found
Cause: Apache/NGINX not configured for PATH_INFO.
Solution (Apache):
/api/.htaccess existsAllowOverride All is set for the API directorysudo systemctl reload apache2Solution (NGINX):
sudo nginx -tsudo systemctl reload nginxSymptoms:
{"error": "API key not authorized for this operation"}
Cause: API key has missing permissions.
Solution:
Symptoms:
{"error": "Attachment not accessible"}
Cause: The file_id does not belong to a ticket attachment.
Solution:
file_id comes from a ticket's thread entry attachment metadataGET /tickets-get.php/{number}.json to get valid file_id valuesThis Plugin is released under the GNU General Public License v2, compatible with osTicket core.
See LICENSE for details.
For questions or issues, please create an issue on GitHub:
Issue Tracker: https://github.com/markus-michalski/osticket-api-endpoints/issues
When reporting issues, please include:
php -v)Developed by Markus Michalski
Contributions welcome!
Development Setup:
# Clone repository
git clone https://github.com/markus-michalski/osticket-api-endpoints.git
cd osticket-api-endpoints
# Install dependencies
composer install
# Run tests
composer test
# Check code style
composer cs-check
See CHANGELOG.md for version history.