Complete REST API reference for the osTicket API Endpoints Plugin. This page provides detailed specifications for all 12 endpoints including request/response schemas, parameter definitions, and practical examples.
Interactive API Explorer: Open Swagger UI to browse endpoints, view schemas, and test requests interactively.
Ticket Management Endpoints:
Subticket Management Endpoints:
8. GET Parent Ticket · Swagger UI
9. GET Child Tickets · Swagger UI
10. POST Create Subticket Link · Swagger UI
11. DELETE Unlink Subticket · Swagger UI
Attachment Endpoints:
12. GET Download Attachment · Swagger UI
All API endpoints require authentication via API Key sent in the X-API-Key header.
X-API-Key: YOUR_API_KEY_HERE
API Key Permissions:
Each endpoint requires specific permissions enabled on your API key:
| Endpoint | Required Permission |
|---|---|
| POST /tickets | can_create_tickets |
| GET /tickets/:number | can_read_tickets |
| PATCH /tickets/:number | can_update_tickets |
| GET /tickets/search | can_search_tickets |
| DELETE /tickets/:number | can_delete_tickets |
| GET /tickets-stats | can_read_stats |
| GET /tickets-statuses | can_read_stats |
| Subticket Endpoints | can_manage_subtickets |
| GET /tickets-attachment-download/:file_id | can_read_tickets |
Configure Permissions:
Admin Panel → Manage → API Keys → [Select Key] → Configure
https://your-domain.com/api
Replace your-domain.com with your osTicket installation domain.
Most GET endpoints support both JSON and XML formats via the URL extension:
.json - JSON response (recommended).xml - XML responseExample:
GET /api/tickets-get.php/123456.json → JSON response
GET /api/tickets-get.php/123456.xml → XML response
| Code | Meaning | Description |
|---|---|---|
200 |
OK | Request successful |
201 |
Created | Resource created successfully |
400 |
Bad Request | Invalid request parameters |
401 |
Unauthorized | Missing or invalid API key |
403 |
Forbidden | API key lacks required permission |
404 |
Not Found | Resource not found |
422 |
Unprocessable Entity | Business logic conflict (e.g., duplicate subticket link) |
500 |
Internal Server Error | Server error occurred |
501 |
Not Implemented | Feature/Plugin not available |
Standard Error Response Format:
{
"error": true,
"message": "Description of what went wrong"
}
Create a new ticket with extended parameters including Markdown formatting, department routing, subticket linking, and file attachments.
POST /api/tickets.json
Required Permission: can_create_tickets
Content-Type: application/json
X-API-Key: YOUR_API_KEY
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
name |
string | Yes | User's full name | "John Doe" |
email |
string | Yes | User's email address | "john@example.com" |
subject |
string | Yes | Ticket subject | "Login Issue" |
message |
string | Yes | Ticket message body | "I cannot log in..." |
phone |
string | No | User's phone number | "+1 555-1234" |
ip |
string | No | Client IP address | "192.168.1.100" |
source |
string | No | Ticket source | "API", "Web", "Email" |
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
format |
string | No | Message format | "text", "html", "markdown" |
departmentId |
string/int | No | Department name or ID | "Support" or 5 |
parentTicketId |
string/int | No | Parent ticket number/ID for subticket | "181752" or 123 |
topicId |
int | No | Help Topic ID | 10 |
priority |
string/int | No | Priority name or ID | "High" or 2 |
duedate |
string | No | Due date (ISO 8601) | "2025-12-31" |
autorespond |
bool | No | Send auto-response email | true, false (default: true) |
attachments |
array | No | File attachments (Base64 data-URI) | See Attachment Format |
Attachments are passed as a JSON array where each element is an object with the filename as key and the Base64 data-URI as value:
{
"attachments": [
{"screenshot.png": "data:image/png;base64,iVBORw0KGgo..."},
{"report.pdf": "data:application/pdf;base64,JVBERi0xLjQ..."}
]
}
Limits:
| Limit | Value |
|---|---|
| Max files per request | 5 |
| Max file size per file | 10 MB |
| Max total size | 50 MB |
Supported MIME types:
| 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 |
"text" - Plain text (default)"html" - HTML formatted text"markdown" - Markdown formatted text (requires Markdown Support Plugin)Success (201 Created): Returns the ticket number as plain text.
781258
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": "Login Issue",
"message": "I cannot access my account",
"phone": "+1 555-1234"
}'
curl -X POST "https://osticket.local/api/tickets.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Smith",
"email": "jane@example.com",
"subject": "Bug Report: Payment Failed",
"message": "## Problem\n\nPayment fails with error:\n\n```\nERROR: Card declined\n```\n\n**Steps to reproduce:**\n1. Add item to cart\n2. Go to checkout\n3. Enter card details\n4. Click Pay\n\n**Expected:** Payment succeeds\n**Actual:** Error message",
"format": "markdown",
"departmentId": "Billing",
"priority": "High"
}'
curl -X POST "https://osticket.local/api/tickets.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Developer",
"email": "dev@example.com",
"subject": "Subtask: Database Migration",
"message": "Migrate production database to new server",
"parentTicketId": "181752",
"departmentId": "Development",
"duedate": "2025-11-30"
}'
Recommended Approach for Subtickets:
While the
parentTicketIdparameter above works for creating subtickets during ticket creation, we strongly recommend using the dedicated Subticket Management Endpoints instead:Benefits of dedicated endpoints:
- ✅ More flexible - Link existing tickets retroactively
- ✅ Better control - Separate permissions (
can_manage_subtickets)- ✅ Full management - View parent, list children, unlink relationships
- ✅ Validation - Comprehensive circular dependency checks
- ✅ Easier workflows - Create tickets independently, then organize
See: POST Create Subticket Link for full documentation.
# Base64-encode file
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",
"departmentId": "Support",
"attachments": [
{"screenshot.png": "data:image/png;base64,'"$FILE_B64"'"}
]
}'
parentTicketId specified, plugin verifies the parent ticket exists.format=markdown and Markdown Support Plugin is not installed/active, returns 501 Not Implemented.autorespond: false to suppress auto-response email to user.Retrieve details of a single ticket including all thread entries.
GET /api/tickets-get.php/{number}.{format}
Required Permission: can_read_tickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
number |
string/int | Yes | Ticket number or ticket ID | "781258" or 123 |
format |
string | Yes | Response format | "json" or "xml" |
Success Response (200 OK):
{
"id": 123,
"number": "781258",
"subject": "Login Issue",
"statusId": 1,
"status": "Open",
"priorityId": 2,
"priority": "Normal",
"departmentId": 5,
"department": "IT Support",
"topicId": 10,
"topic": "Technical Support",
"userId": 45,
"user": {
"name": "John Doe",
"email": "john@example.com"
},
"staffId": 3,
"staff": "Jane Smith",
"teamId": 2,
"team": "Level 1 Support",
"slaId": 1,
"sla": "Standard SLA",
"created": "2025-11-08 10:30:00",
"updated": "2025-11-08 14:20:00",
"duedate": "2025-11-15 10:30:00",
"closed": null,
"isOverdue": false,
"isAnswered": true,
"source": "API",
"ip": "192.168.1.100",
"children": [124, 125],
"thread": [
{
"id": 456,
"type": "message",
"poster": "John Doe",
"timestamp": "2025-11-08 10:30:00",
"body": "I cannot access my account",
"userId": 45,
"attachments": [
{
"id": 12,
"file_id": 29,
"filename": "screenshot.png",
"size": 45320,
"mime_type": "image/png",
"inline": false
}
],
"attachment_count": 1
},
{
"id": 457,
"type": "response",
"poster": "Jane Smith",
"timestamp": "2025-11-08 11:00:00",
"body": "Please try resetting your password",
"staffId": 3,
"staff": "Jane Smith"
},
{
"id": 458,
"type": "note",
"poster": "Admin",
"timestamp": "2025-11-08 11:15:00",
"body": "User has multiple failed login attempts",
"staffId": 1,
"staff": "Admin"
}
]
}
Thread Entry Types:
"message" - User message"response" - Staff response"note" - Internal note (staff only)Thread Entry Attachment Metadata:
Thread entries that have file attachments include an attachments array and an attachment_count field. Each attachment object contains:
| Field | Type | Description |
|---|---|---|
id |
integer | Internal attachment ID |
file_id |
integer | File ID — use with Download Attachment endpoint |
filename |
string | Original filename |
size |
integer | File size in bytes |
mime_type |
string | MIME type (e.g., "image/png", "application/pdf") |
inline |
boolean | Whether the attachment is inline (embedded in the message body) |
Thread entries without attachments do not include the
attachmentsorattachment_countfields.
# By ticket number (JSON)
curl "https://osticket.local/api/tickets-get.php/781258.json" \
-H "X-API-Key: YOUR_API_KEY"
# By ticket ID (JSON)
curl "https://osticket.local/api/tickets-get.php/123.json" \
-H "X-API-Key: YOUR_API_KEY"
# As XML
curl "https://osticket.local/api/tickets-get.php/781258.xml" \
-H "X-API-Key: YOUR_API_KEY"
lookupByNumber(), then falls back to lookup() (by ID).file_id for downloading via the attachment download endpoint.children array contains internal database IDs of child tickets (not ticket numbers).staff, team, sla, duedate, closed, ip can be null.Update ticket properties such as status, department, priority, SLA plan, staff assignment, due date, and add notes with attachments.
PATCH /api/tickets-update.php/{number}.{format}
Also accepts PUT method.
Required Permission: can_update_tickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
number |
string | Yes | Ticket number | "781258" |
format |
string | Yes | Response format | "json" or "xml" |
All fields are optional - only include fields you want to update.
| Field | Type | Description | Example |
|---|---|---|---|
statusId |
string/int | Status name or ID | "Resolved", "Closed" 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 |
priority |
string/int | Priority name or ID | "Urgent" or 4 |
dueDate |
string/null | Due date (ISO 8601). Set to null to clear. |
"2025-12-31" |
parentTicketNumber |
string | Parent ticket number (makes this a subticket) | "781200" |
note |
string | Add internal note (staff only) | "Customer called for update" |
noteTitle |
string | Title for the internal note | "Status Update" (default: "API Update") |
noteFormat |
string | Format for the note | "markdown", "html", "text" (default: "markdown") |
attachments |
array | File attachments (requires note) |
See Attachment Format |
Success (200 OK): Returns the updated ticket number as plain text.
781258
# Update status
curl -X PATCH "https://osticket.local/api/tickets-update.php/781258.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"statusId": "Resolved"}'
# Assign to staff with note
curl -X PATCH "https://osticket.local/api/tickets-update.php/781258.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"staffId": "john.doe",
"note": "Assigned to John for follow-up"
}'
# Note with attachment
FILE_B64=$(base64 -w 0 /path/to/audit-report.pdf)
curl -X PATCH "https://osticket.local/api/tickets-update.php/781258.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"'"}
]
}'
statusId, departmentId, slaId, staffId accept human-readable names (case-insensitive). Falls back to ID lookup.note field adds a note visible only to staff.attachments is provided without note, the API returns 400 Bad Request.dueDate to null to remove an existing due date. The overdue flag is automatically cleared when setting a future due date.Search tickets by query string, status, department, and other filters. Supports pagination.
GET /api/tickets-search.php
Required Permission: can_search_tickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
query |
string | No | Search in ticket subject | "login", "payment failed" |
status |
string/int | No | Filter by status name or ID | "Open" or 1 |
department |
string/int | No | Filter by department name, ID, or path | "Support", 5, "Dev / osTicket" |
limit |
int | No | Maximum results (default: 20, max: 100) | 50 |
offset |
int | No | Pagination offset | 0, 20, 40 |
sort |
string | No | Sort field | "created", "updated", "number" |
Returns a direct JSON array (not wrapped in an object):
[
{
"id": 123,
"number": "781258",
"subject": "Login Issue",
"statusId": 1,
"status": "Open",
"priorityId": 2,
"priority": "Normal",
"departmentId": 5,
"department": "IT Support",
"topicId": 10,
"topic": "Technical Support",
"created": "2025-11-08 10:00:00",
"updated": "2025-11-08 14:30:00",
"dueDate": "2025-11-15 10:00:00",
"staffId": 3,
"staff": "Jane Smith",
"teamId": 2,
"team": "Level 1 Support",
"slaId": 1,
"sla": "Standard SLA",
"isOverdue": false,
"isAnswered": true
}
]
# Basic search
curl "https://osticket.local/api/tickets-search.php?query=login" \
-H "X-API-Key: YOUR_API_KEY"
# Filter by status
curl "https://osticket.local/api/tickets-search.php?status=Open" \
-H "X-API-Key: YOUR_API_KEY"
# Multiple filters with pagination
curl "https://osticket.local/api/tickets-search.php?query=payment&status=Open&department=Support&limit=50" \
-H "X-API-Key: YOUR_API_KEY"
# Page 2
curl "https://osticket.local/api/tickets-search.php?status=Open&limit=20&offset=20" \
-H "X-API-Key: YOUR_API_KEY"
query searches in ticket subject only (case-insensitive)."Parent / Child" for nested departments.created descending (newest first).Permanently delete a ticket from the database.
WARNING: This action is irreversible! Ticket and all thread entries will be permanently deleted.
DELETE /api/tickets-delete.php/{number}.{format}
Required Permission: can_delete_tickets
Success (200 OK): Returns the deleted ticket number as plain text.
781258
curl -X DELETE "https://osticket.local/api/tickets-delete.php/781258.json" \
-H "X-API-Key: YOUR_API_KEY"
can_delete_tickets permission is disabled by default on all API keys.PATCH to set status to "Closed" or "Archived" instead.Retrieve comprehensive ticket statistics including global counts, department breakdown, and staff performance.
GET /api/tickets-stats.php
Required Permission: can_read_stats
{
"total": 150,
"open": 45,
"closed": 105,
"overdue": 3,
"by_department": {
"Billing": {
"total": 70,
"open": 25,
"closed": 45,
"overdue": 2
},
"Support": {
"total": 80,
"open": 20,
"closed": 60,
"overdue": 1
}
},
"by_staff": [
{
"staff_id": 1,
"staff_name": "John Smith",
"total": 35,
"departments": {
"Support": {
"open": 10,
"closed": 20,
"overdue": 0
}
}
}
]
}
by_department is an object keyed by department name (sorted alphabetically).by_staff is an array sorted alphabetically by staff name.Retrieve all available ticket statuses with IDs and states.
GET /api/tickets-statuses.php
Required Permission: can_read_stats
[
{
"id": 1,
"name": "Open",
"state": "open"
},
{
"id": 3,
"name": "Closed",
"state": "closed"
}
]
"open" or "closed".The following endpoints require the Subticket Manager Plugin to be installed and active. If plugin is not available, endpoints return 501 Not Implemented.
Retrieve the parent ticket of a child ticket (subticket).
GET /api/tickets-subtickets-parent.php/{number}.{format}
Required Permission: can_manage_subtickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
number |
string | Yes | Child ticket number | "781259" |
format |
string | Yes | Response format | "json" or "xml" |
{
"parent": {
"ticket_id": 100,
"number": "781200",
"subject": "Parent Ticket",
"status": "Open"
}
}
Returns {"parent": null} if the ticket has no parent.
Retrieve all child tickets (subtickets) of a parent ticket.
GET /api/tickets-subtickets-list.php/{number}.{format}
Required Permission: can_manage_subtickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
number |
string | Yes | Parent ticket number | "781200" |
format |
string | Yes | Response format | "json" or "xml" |
{
"children": [
{
"ticket_id": 124,
"number": "781259",
"subject": "Child Ticket 1",
"status": "Open"
},
{
"ticket_id": 125,
"number": "781260",
"subject": "Child Ticket 2",
"status": "Closed"
}
]
}
Returns {"children": []} if no subtickets exist.
Create a parent-child relationship between two existing tickets.
POST /api/tickets-subtickets-create.php/{parentNumber}.{format}
Required Permission: can_manage_subtickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
parentNumber |
string | Yes | Parent ticket number | "781200" |
format |
string | Yes | Response format | "json" |
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
childId |
string | Yes | Child ticket number | "781259" |
{
"success": true,
"message": "Subticket relationship created successfully",
"parent": {
"ticket_id": 100,
"number": "781200",
"subject": "Parent Ticket",
"status": "Open"
},
"child": {
"ticket_id": 124,
"number": "781259",
"subject": "Child Ticket",
"status": "Open"
}
}
{
"error": true,
"message": "Subticket relationship already exists"
}
curl -X POST "https://osticket.local/api/tickets-subtickets-create.php/781200.json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"childId": "781259"}'
Remove the parent-child relationship of a child ticket, making it a standalone ticket again.
DELETE /api/tickets-subtickets-unlink.php/{number}.{format}
Required Permission: can_manage_subtickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
number |
string | Yes | Child ticket number to unlink | "781259" |
format |
string | Yes | Response format | "json" |
{
"success": true,
"message": "Subticket relationship removed successfully",
"child": {
"ticket_id": 124,
"number": "781259",
"subject": "Child Ticket",
"status": "Open"
}
}
curl -X DELETE "https://osticket.local/api/tickets-subtickets-unlink.php/781259.json" \
-H "X-API-Key: YOUR_API_KEY"
Download a ticket attachment by file ID. Returns file metadata and Base64-encoded file content.
GET /api/tickets-attachment-download.php/{file_id}.{format}
Required Permission: can_read_tickets
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
file_id |
integer | Yes | File ID from attachment metadata | 29 |
format |
string | Yes | Response format | "json" or "xml" |
How to find the file_id: Use the GET Retrieve Ticket endpoint. Thread entries with attachments include an
attachmentsarray where each item has afile_idfield.
Success Response (200 OK):
{
"file_id": 29,
"filename": "screenshot.png",
"mime_type": "image/png",
"size": 45320,
"content": "iVBORw0KGgoAAAANSUhEUgAA..."
}
| Field | Type | Description |
|---|---|---|
file_id |
integer | File ID |
filename |
string | Original filename |
mime_type |
string | MIME type |
size |
integer | File size in bytes |
content |
string | Base64-encoded file content |
| Code | Condition | Example Message |
|---|---|---|
400 |
Invalid file ID (< 1) | "Invalid file ID" |
403 |
File not linked to a ticket attachment | "Attachment not accessible" |
404 |
File not found in database | "File not found" |
500 |
Failed to read file content | "Failed to read file content" |
The endpoint verifies that the requested
file_idexists in theost_ticket_attachmenttable before serving any content. This prevents arbitrary access to files in theost_filetable that are not ticket attachments (e.g., canned responses, logos, or knowledge base files).
curl "https://osticket.local/api/tickets-attachment-download.php/29.json" \
-H "X-API-Key: YOUR_API_KEY"
# Download and extract Base64 content, then decode to file
curl -s "https://osticket.local/api/tickets-attachment-download.php/29.json" \
-H "X-API-Key: YOUR_API_KEY" \
| jq -r '.content' \
| base64 -d > screenshot.png
# Step 1: Get ticket with attachment metadata
curl -s "https://osticket.local/api/tickets-get.php/781258.json" \
-H "X-API-Key: YOUR_API_KEY" \
| jq '.thread[] | select(.attachments) | .attachments[] | {file_id, filename, size}'
# Example output:
# {"file_id": 29, "filename": "screenshot.png", "size": 45320}
# {"file_id": 30, "filename": "error-log.txt", "size": 1024}
# Step 2: Download specific attachment by file_id
curl -s "https://osticket.local/api/tickets-attachment-download.php/29.json" \
-H "X-API-Key: YOUR_API_KEY" \
| jq -r '.content' \
| base64 -d > screenshot.png
If you use NGINX as a reverse proxy, you need to add a rewrite rule for this endpoint:
location /api/ {
# ... existing rules ...
rewrite ^/api/tickets-attachment-download.php/(.*)$ /api/tickets-attachment-download.php/$1 break;
}
can_read_tickets permission — no separate permission is needed.inline flag from the attachment metadata to distinguish them.The API does not implement built-in rate limiting. Consider implementing rate limiting at the web server level:
# In http block
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# In server block
location /api/ {
limit_req zone=api burst=20 nodelay;
# ... other configuration
}
Last Updated: 2026-03-03