-
Notifications
You must be signed in to change notification settings - Fork 15
REST API Tickets
The complete usage guide for the Tickets module of the REST API β every URL, method, parameter and response shape. This mirrors the interactive documentation built into the product (System β API β Documentation), where each endpoint also has a live "Try it" tester.
Note
This is the first of the per-module API guides. As the API grows (assets, problems, changesβ¦), each module gets its own page like this one.
1. Create a key under System β API (grant it only what it needs β for the example below, just Tickets: create). 2. Copy the key when it's shown β that's the only time you'll see it. 3. Call the API:
curl -X POST "https://your-server/api/v1/tickets" \
-H "Authorization: Bearer fitsm_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"subject": "Disk space low on SQL01",
"requester_email": "alerts@example.com",
"description": "C: drive below 5% free.",
"priority": "High"
}'That's a ticket in the inbox β with the description as its opening message, an audit entry, and any matching workflows already fired. π
| Base URL |
https://your-server/api/v1 β shown on the System β API page for your install. No mod_rewrite? Use β¦/api/v1/index.php/<path> instead; identical behaviour. |
| Authentication |
Authorization: Bearer fitsm_β¦ on every request (X-Api-Key: fitsm_β¦ also accepted). |
| Format | JSON in, JSON out (UTF-8). Timestamps are always ISO 8601 UTC: 2026-07-02T22:18:22Z. |
| Success |
{"data": β¦} β plus "meta" (pagination) on list endpoints. Status 200, or 201 on creation. |
| Errors |
{"error": {"code": "β¦", "message": "β¦"}} with a real status: 401 bad key Β· 403 missing permission (named in the message) Β· 404 not found/not visible Β· 405 wrong verb Β· 409 conflict Β· 422 invalid input Β· 429 rate limited. |
| Rate limit | 60 requests/min per key by default (per-key override available). Watch X-RateLimit-Remaining; a 429 means slow down. |
| Permissions | Each endpoint below shows its required permission as a badge, e.g. π tickets.read. |
| Companies (MSP) | On multi-company installs a key only sees its scoped companies. null company fields mean the Default company. Single-company installs can ignore everything about company_id. |
| PATCH semantics | Send only the fields you want to change. Omitted = untouched; explicit null (or "") = cleared. Re-sending the same PATCH is safe β no duplicate audit rows or emails. |
| Name or id | Lookups accept either: "priority": "High" or "priority_id": 3. |
Verifies authentication and returns what the key can do β its name, the analyst it acts as, its permission map, company scope and expiry. Any valid key can call it.
{ "data": { "ok": true,
"key": { "name": "Monitoring", "acts_as": "Administrator",
"permissions": { "tickets": ["read", "create"] },
"companies": null, "expires_at": null },
"server_time": "2026-07-02T22:18:09Z" } }The API version and the full list of routes.
Trash is excluded unless deleted=true. Results are limited to the key's company scope.
| Parameter | Description |
|---|---|
state |
open | closed | all (default all) |
status / status_id
|
Filter by status name (e.g. In Progress) or id |
priority / priority_id
|
Filter by priority name or id |
assigned_analyst_id |
Filter by assignee Β· unassigned=true finds unassigned tickets |
requester_email / user_id
|
Filter by requester |
ticket_type_id Β· origin_id Β· department_id Β· company_id
|
More id filters |
q |
Search subject and ticket number |
created_since Β· created_before Β· updated_since Β· closed_since
|
ISO 8601 date-time bounds |
deleted |
true lists the trash instead |
sort |
created_at, updated_at, closed_at, id, subject, ticket_number, priority, status β prefix - for descending (default -created_at) |
page / per_page
|
Pagination β default 1 / 25, max per_page 100 |
GET /tickets?state=open&priority=High&updated_since=2026-07-01T00:00:00Z&sort=-updated_at&per_page=50
{ "data": [ { "id": 139, "ticket_number": "WBK-509-34271",
"subject": "API test - printer jam",
"status": { "id": 2, "name": "In Progress", "is_closed": false },
"priority": { "id": 3, "name": "High" },
"ticket_type": null, "origin": null, "department": null,
"assigned_analyst": { "id": 1, "name": "Administrator" },
"requester": { "id": 173, "email": "jane@example.com", "name": "Jane Smith" },
"company": { "id": 1, "name": "Default" },
"first_time_fix": null, "it_training_provided": null,
"created_at": "2026-07-02T22:18:22Z", "updated_at": "2026-07-02T22:18:47Z",
"closed_at": null, "work_start_at": null, "deleted_at": null } ],
"meta": { "page": 1, "per_page": 50, "total": 1, "total_pages": 1 } }Creates a ticket exactly like the UI: the requester is found or created by email, the description becomes the ticket's opening message (so it reads like any other ticket in the inbox), an audit entry is written, and the workflow engine fires ticket.created. Defaults: status Open, priority Normal, unassigned, the key's default company. Returns 201 with the full ticket.
| Field | Required | Description |
|---|---|---|
subject |
β | Ticket subject |
requester_email |
β | Requester's email β found or created automatically |
requester_name |
Display name, used if the requester is new | |
description |
The request text (plain text; becomes the ticket body) | |
status / status_id
|
Override the default Open | |
priority / priority_id
|
Override the default Normal | |
ticket_type_id Β· origin_id Β· department_id
|
Optional classification | |
assigned_analyst_id |
Assign on creation | |
company_id |
File under a specific company (must be in the key's scope) | |
mailbox_id |
Send-from mailbox for later replies (validated against the company) |
Everything in the list shape plus description_html β the original request body.
Updates any combination of fields, with full UI parity: closing sets closed_at (and reopening clears it), assignment keeps the owner in sync and sends the assignment email, closing sends the closure email and can auto-trigger a CSAT survey, every change lands in the audit log, and the workflow engine fires ticket.status_changed / ticket.priority_changed / ticket.assigned. Returns the updated ticket.
| Field | Notes |
|---|---|
subject |
Cannot be blanked |
status / status_id
|
By name or id |
priority / priority_id
|
null clears |
ticket_type_id Β· origin_id Β· department_id
|
null clears |
assigned_analyst_id |
null unassigns |
first_time_fix Β· it_training_provided
|
Booleans |
work_start_at |
Scheduled work start, ISO 8601 (null clears) |
company_id |
Move to another company β the key must be scoped to both sides |
{ "status": "In Progress", "priority": "High", "assigned_analyst_id": 1 }Warning
A ticket in the trash answers 409 conflict to updates β restore it first.
Soft-deletes (moves to the trash β same as the UI, fully restorable). Already trashed β 409.
Brings a ticket back from the trash and returns it.
All notes, oldest first β is_internal: true marks private analyst notes.
| Field | Required | Description |
|---|---|---|
text |
β | The note text |
is_internal |
false = public note (default true) |
The note is attributed to the analyst the key acts as. Returns 201.
The full conversation β inbound emails / channel messages (channel is email, whatsapp, β¦) and outbound replies β oldest first, with HTML bodies:
{ "data": [ { "id": 512, "direction": "Inbound", "is_initial": true, "channel": "email",
"subject": "Printer jam", "from": { "address": "jane@example.com", "name": "Jane Smith" },
"to": "support@company.com", "cc": null,
"body_preview": "Paper jam error, tray 2.", "body_html": "<p>Paper jam error, tray 2.</p>",
"received_at": "2026-07-02T09:14:03Z" } ] }Every audited change with old/new values and who made it:
{ "data": [ { "id": 120, "field": "Status", "old_value": "Open", "new_value": "In Progress",
"analyst": { "id": 1, "name": "Administrator" },
"created_at": "2026-07-02T22:18:47Z" } ] }The live SLA computation β the same engine the UI uses: response/resolution targets from the ticket's priority, elapsed and remaining business minutes against the SLA calendar (with paused statuses honoured), percentages and breach flags.
Active time entries with analyst, minutes, notes and timestamp.
| Field | Required | Description |
|---|---|---|
minutes |
β | Positive integer |
notes |
What the time was spent on | |
entry_at |
When the work happened, ISO 8601 (default now) |
Soft-deletes the entry (same as the UI).
End users who raise tickets (distinct from analysts). POST /tickets creates requesters automatically β these endpoints are for lookups and directory sync.
| Parameter | Description |
|---|---|
q |
Search email and names |
email |
Exact email lookup |
page / per_page
|
Pagination |
One requester, plus their ticket counts (within the key's company scope):
{ "data": { "id": 173, "email": "jane@example.com", "display_name": "Jane Smith",
"preferred_name": null, "created_at": "2026-07-02T22:18:22Z",
"tickets": { "total": 12, "open": 2 } } }email (required, unique β duplicate β 409), display_name, preferred_name. Returns 201.
Change email (uniqueness enforced), display_name, preferred_name.
Everything an integration needs to build valid writes β grab these once and cache them.
| Endpoint | Permission | Returns |
|---|---|---|
π’ GET /analysts
|
π analysts.read |
Active analysts (id, name, email) β assignment targets |
π’ GET /companies
|
π companies.read |
The companies this key can see (id, name, is_default) |
π’ GET /statuses
|
π reference.read |
Statuses with is_closed, is_default, pauses_sla, colour
|
π’ GET /priorities
|
π reference.read |
Priorities with SLA response/resolution targets (minutes) |
π’ GET /ticket-types
|
π reference.read |
Ticket types β pass ?company_id= to apply that company's add/hide overrides |
π’ GET /origins
|
π reference.read |
Ticket origins β ?company_id= as above |
π’ GET /departments
|
π reference.read |
Departments |
A typical "alert β ticket β auto-resolve" round trip using a key with just tickets: read, create, update:
B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_β¦"
# 1. Alert fires β raise a High ticket
TICKET=$(curl -s -X POST "$B/tickets" -H "$K" -H "Content-Type: application/json" -d '{
"subject": "[ALERT] SQL01 disk C: below 5%",
"requester_email": "alerts@example.com",
"description": "Datastore free space 4.2%. Threshold 5%.",
"priority": "High"}' | jq -r '.data.id')
# 2. Alert recovers β find our open ticket and close it with a note*
curl -s -X PATCH "$B/tickets/$TICKET" -H "$K" -H "Content-Type: application/json" \
-d '{"status": "Closed"}'* closing via the API sends the closure email and fires your workflows, exactly as if an analyst clicked it. Give the key ticket_notes: create too if you want the recovery details on the ticket.
Under the hood: REST API β How It Works. Keys & permissions: System β API in the product (System module). Live tester: System β API β Documentation.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)