Markus Michalski FAQ

API Endpoints: API Documentation

REST API reference of the osTicket API Endpoints plugin: all 12 endpoints with paths, methods, parameters, permissions, status codes and examples.

Deutsche Version

Back to overview

API Endpoints: API Documentation

This documentation always describes the latest published version.

This page describes all 12 endpoints of the plugin: paths, methods, parameters, required API key permissions, responses and error codes. Installation and web server setup are covered on Installation, the permissions on Configuration.

Overview

# Method Path Permission
1 POST /api/tickets.json see POST Create Ticket
2 GET /api/tickets-get.php/{number}.{format} can_read_tickets
3 PATCH, PUT /api/tickets-update.php/{number}.{format} or /api/tickets/{number}.{format} can_update_tickets
4 GET /api/tickets-search.php can_search_tickets or can_read_tickets
5 DELETE /api/tickets-delete.php/{number}.{format} can_delete_tickets
6 GET /api/tickets-stats.php can_read_stats or can_read_tickets
7 GET /api/tickets-statuses.php can_read_stats or can_read_tickets
8 GET /api/tickets-subtickets-parent.php/{number}.{format} can_manage_subtickets
9 GET /api/tickets-subtickets-list.php/{number}.{format} can_manage_subtickets
10 POST /api/tickets-subtickets-create.php/{parentNumber}.{format} can_manage_subtickets
11 DELETE /api/tickets-subtickets-unlink.php/{number}.{format} can_manage_subtickets
12 GET /api/tickets-attachment-download.php/{file_id}.{format} can_read_tickets

{format} is json or xml. The permission names are the columns of the API key table, see Configuration.

Authentication

Every request carries the API key in the X-API-Key header.

X-API-Key: YOUR_API_KEY

The standalone endpoints (2 to 12) reject a missing or invalid key with 401. An API key without the permission required by an endpoint receives 401 with a message such as API key not authorized to read tickets. Only the subticket endpoints and the attachment download answer with 403 in specific cases, see the status codes. Endpoint 1 is handled differently, see its section.

can_read_tickets also grants search, statistics and the status list. A key that only has can_search_tickets or can_read_stats can use just that one endpoint. All other permissions have no fallback: a missing or disabled permission means access is denied.

Base URL

https://your-domain.com/api

Response Formats

Endpoint type Success response
Create (1), update (3), delete (5) Ticket number as plain text (text/plain)
Endpoints with {number}.{format} (2, 8, 9, 10, 11, 12) JSON, or XML when the path ends in .xml
Search, statistics, status list (4, 6, 7) JSON. The path info after the script name is optional and may be .json or .xml, with .xml the response is XML

XML responses use response as root element, list entries are rendered as item. Errors raised by the plugin itself are JSON, even for .xml requests:

{
  "error": true,
  "message": "Description of what went wrong"
}

Errors of the API key check (missing or invalid key, IP restriction) come from osTicket core or from the API Key Wildcard plugin and use their body format.

Errors from the two routes registered through the signal (create and PATCH /api/tickets/{number}.{format}) are returned as plain text containing only the message.

Status Codes

Code Meaning When
200 OK Request successful
201 Created Ticket created (endpoint 1)
400 Bad Request Invalid URL format, invalid or missing parameters, failed validation
401 Unauthorized Standalone endpoints and the update route: missing or invalid API key, or the key lacks the required permission
403 Forbidden Subticket endpoints: missing can_manage_subtickets, or the API key object provides department restrictions that exclude the ticket's department. Attachment download: no attachment row for the file
404 Not Found Ticket, department, status, topic, SLA, staff member or file not found
405 Method Not Allowed Wrong HTTP method for a standalone endpoint (Method Not Allowed. Use GET)
422 Unprocessable Entity Subticket link already exists, or the child already has a different parent
500 Internal Server Error Operation failed on the server
501 Not Implemented Subticket plugin not available

Lookup by Name

departmentId, statusId, topicId and slaId accept a name or an ID. A value that is not numeric is looked up as a name (case-insensitive) and returns 404 if nothing matches. A numeric value, also when sent as a string, is always used as an ID. staffId accepts the staff ID, the username or the email address, not the display name.


1. POST Create Ticket

Creates a ticket. The plugin adds format, departmentId and parentTicketNumber to osTicket's own creation parameters.

Endpoint

POST /api/tickets.json

Permission: For this signal route the plugin itself checks neither the API key nor the permission can_create_tickets. Whether osTicket's core checks the key before dispatching the signal cannot be derived from this plugin's code.

Send the body as JSON with Content-Type: application/json.

Parameters handled by the plugin

Parameter Type Description Example
format string Message format: text, html or markdown. Aliases: md for markdown, plain and txt for text "markdown"
departmentId string or int Department name or ID. Active departments only, sub-departments are found by their name "Support" or 5
parentTicketNumber string or int Number of the parent ticket (an internal ID is accepted as fallback). The parent must exist and must not be a child itself "181752"

Parameters handled by osTicket core

All other fields are processed by osTicket's own ticket creation, for example name, email, subject, message, topicId and attachments. Which of them are required depends on your osTicket ticket form.

Attachments are passed as an array of objects, each with the file name as key and a Base64 data URI as value:

{
  "attachments": [
    {"screenshot.png": "data:image/png;base64,iVBORw0KGgo..."},
    {"report.pdf": "data:application/pdf;base64,JVBERi0xLjQ..."}
  ]
}

Response

201 Created: the ticket number as plain text.

781258

Examples

# Basic ticket
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"
  }'

# Markdown message, routed to a department
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 CARD_DECLINED.",
    "format": "markdown",
    "departmentId": "Billing"
  }'

# Create as a subticket of an existing ticket
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 the production database",
    "parentTicketNumber": "181752",
    "departmentId": "Development"
  }'

Notes


2. GET Retrieve Ticket

Returns one ticket including its thread.

GET /api/tickets-get.php/{number}.{format}

Permission: can_read_tickets

Parameter Type Description
number string or int Ticket number. If no ticket has that number, the value is tried as an internal ticket ID
format string json or xml

Response

{
  "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": "M",
      "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
    }
  ]
}
Field Description
thread[].type Thread entry type as stored by osTicket: M message, R response, N internal note
thread[].staffId, thread[].staff Only present for entries written by a staff member
thread[].userId Only present for entries written by a user
thread[].attachments, thread[].attachment_count Only present when the entry has attachments. file_id is the value for the attachment download
children Internal IDs (not numbers) of the child tickets
closed Close date, only when the ticket is closed, otherwise null

Example

curl "https://osticket.local/api/tickets-get.php/781258.json" \
  -H "X-API-Key: YOUR_API_KEY"

3. PATCH Update Ticket

Updates properties of a ticket and optionally adds an internal note with attachments.

PATCH /api/tickets-update.php/{number}.{format}
PUT   /api/tickets-update.php/{number}.{format}

The same update is available through the route PATCH /api/tickets/{number}.json (also PUT, .xml accepted). That route answers errors as plain text instead of JSON.

Permission: can_update_tickets

Send the body as JSON with Content-Type: application/json. Only include the fields you want to change.

Field Type Description
statusId string or int Status name or ID
departmentId string or int Department name or ID
topicId string or int Help topic name or ID
slaId string or int SLA plan name or ID
staffId string or int Staff ID, username or email address
dueDate string or null Due date in a format PHP's DateTime understands, for example 2025-12-31 or 2025-12-31T17:30:00. null or an empty string removes the due date
parentTicketNumber string Ticket number of the parent (makes the ticket a subticket)
note string Internal note. Posted as API, shown to staff only
noteTitle string Title of the note, default API Update
noteFormat string Format of the note, default markdown
attachments array Attachments of the note, same format as on creation. Only used together with note, otherwise silently ignored

Other fields, priority included, are ignored without an error.

Response

200 OK: the ticket number as plain text.

Examples

# Change 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 a staff member with a 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"}'

# Remove the due date
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 '{"dueDate": null}'

Notes


4. GET Search Tickets

GET /api/tickets-search.php

Permission: can_search_tickets or can_read_tickets

Parameter Type Default Description
query string Case-insensitive search in the ticket subject
status string or int Status name or ID
department string or int Department name, ID or path such as Development / osTicket
limit int 20 Maximum results. Values below 1 become 20, values above 100 become 100
offset int 0 Pagination offset
sort string created created (newest first), updated (most recently updated first) or number (ascending). Other values fall back to created

A status or department name that matches nothing is ignored and does not filter.

Response

A JSON array, not wrapped in an object. Entries carry no thread:

[
  {
    "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
  }
]

Examples

curl "https://osticket.local/api/tickets-search.php?query=login" \
  -H "X-API-Key: YOUR_API_KEY"

curl "https://osticket.local/api/tickets-search.php?query=payment&status=Open&department=Support&limit=50&offset=50" \
  -H "X-API-Key: YOUR_API_KEY"

5. DELETE Delete Ticket

Permanently deletes a ticket.

This action cannot be undone. The ticket and its thread are removed from the database.

DELETE /api/tickets-delete.php/{number}.{format}

Permission: can_delete_tickets, disabled by default for every API key.

200 OK: the deleted ticket number as plain text.

curl -X DELETE "https://osticket.local/api/tickets-delete.php/781258.json" \
  -H "X-API-Key: YOUR_API_KEY"

Consider a PATCH to a closed status instead of deleting.


6. GET Ticket Statistics

GET /api/tickets-stats.php

Permission: can_read_stats or can_read_tickets

{
  "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}
      }
    }
  ]
}

7. GET List Statuses

GET /api/tickets-statuses.php

Permission: can_read_stats or can_read_tickets

[
  {"id": 1, "name": "Open", "state": "open"},
  {"id": 3, "name": "Closed", "state": "closed"}
]

The list is sorted by the status sort order and includes custom statuses. state is the osTicket state of the status.


Subticket Endpoints

The endpoints 8 to 11 require the Subticket Manager plugin (class SubticketPlugin) and the permission can_manage_subtickets. They check in this order: input validation first (an invalid ticket number answers 400, endpoint 10 additionally answers 400 for a missing childId and for linking a ticket to itself), then the permission (403, API key not authorized for subticket operations), then the plugin (501, Subticket plugin not available), then the ticket lookups. Endpoints 10 and 11 additionally check the ticket's department, but only if the API key object provides department restrictions. Standard osTicket API keys have none.

8. GET Parent Ticket

GET /api/tickets-subtickets-parent.php/{number}.{format}

number is the child ticket number.

{
  "parent": {
    "ticket_id": 100,
    "number": "781200",
    "subject": "Parent Ticket",
    "status": "Open"
  }
}

Returns {"parent": null} if the ticket has no parent. 404 (Child ticket not found) if the ticket does not exist.

9. GET Child Tickets

GET /api/tickets-subtickets-list.php/{number}.{format}

number is the parent ticket number.

{
  "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 there are none. 404 (Parent ticket not found) if the ticket does not exist.

10. POST Create Subticket Link

Links two existing tickets.

POST /api/tickets-subtickets-create.php/{parentNumber}.{format}
Body field Type Required Description
childId string Yes Ticket number of the child ticket
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"}'
{
  "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"}
}
Code Message Cause
400 Missing childId in request body childId missing
400 Cannot link ticket to itself Parent and child are the same ticket
403 Access denied to parent ticket department or ... child ticket department Only if the API key object provides department restrictions that exclude the department
404 Parent ticket not found or Child ticket not found Ticket does not exist
422 Subticket relationship already exists The link is already there
422 Child ticket already has a different parent The child has another parent

Removes the parent link of a child ticket.

DELETE /api/tickets-subtickets-unlink.php/{number}.{format}

number is the child ticket number.

{
  "success": true,
  "message": "Subticket relationship removed successfully",
  "child": {"ticket_id": 124, "number": "781259", "subject": "Child Ticket", "status": "Open"}
}
Code Message Cause
403 Access denied to child ticket department Only if the API key object provides department restrictions that exclude the department
404 Child ticket not found Ticket does not exist
404 Child has no parent to unlink The ticket has no parent

Attachment Endpoint

12. GET Download Attachment

Returns a file as Base64 together with its metadata.

GET /api/tickets-attachment-download.php/{file_id}.{format}

Permission: can_read_tickets

Parameter Type Description
file_id integer file_id from the attachment metadata of a thread entry, see GET Retrieve Ticket
format string json or xml
{
  "file_id": 29,
  "filename": "screenshot.png",
  "mime_type": "image/png",
  "size": 45320,
  "content": "iVBORw0KGgoAAAANSUhEUgAA..."
}
Code Message Cause
400 Invalid file ID file_id smaller than 1
403 Attachment not accessible No row with this file_id exists in the attachment table
404 File not found File does not exist
500 Failed to read file content File content could not be read

Before serving a file, the endpoint checks that a row with this file_id exists in the attachment table. The check does not filter by the type of the object the attachment belongs to.

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

The whole file is read into memory and Base64 encoded, which makes the response about a third larger than the file.

Rate Limiting

The plugin implements no rate limiting. If you need it, configure it on the web server.