Tickets
API reference for tickets, ticket replies and technicians - raising, updating, replying to, resolving and closing tickets, and the time worked on them.
These endpoints need the Support area of the key's role. See Keys and permissions.
Tickets
| Endpoint | Does |
|---|---|
GET tickets/read.php | Lists tickets, or gets one |
POST tickets/create.php | Raises a ticket |
POST tickets/update.php | Changes a ticket |
POST tickets/resolve.php | Resolves a ticket |
POST tickets/close.php | Closes a ticket |
Tickets can't be deleted through the API.
Read
| Parameter | Does |
|---|---|
ticket_id | Just this ticket, with its status's name and colour |
client_id | Just this client's tickets |
limit, offset | Paging |
The list has every ticket - new, open, resolved and closed. ticket_status is the status's number: 1 New, 2 Open, 3 On Hold, 4 Resolved and 5 Closed, then any you've added in Admin > Ticket Statuses.
curl -G https://itflow.example.com/api/v1/tickets/read.php \
--data-urlencode "api_key=YOUR_API_KEY" \
--data-urlencode "ticket_id=93"
{
"success": "True",
"count": 1,
"data": [
{
"ticket_id": "93",
"ticket_prefix": "TCK-",
"ticket_number": "93",
"ticket_source": "Email",
"ticket_subject": "Site internet dropping intermittently",
"ticket_priority": "High",
"ticket_status": "2",
"ticket_status_name": "Open",
"ticket_billable": "1",
"ticket_created_at": "2026-10-15 11:25:00",
"ticket_resolution_due_at": "2026-10-15 19:25:00",
"ticket_assigned_to": "1",
"ticket_contact_id": "4",
"ticket_asset_id": "1",
"ticket_client_id": "1",
"tags": [ { "tag_id": 116, "tag_name": "Waiting on Vendor", "tag_kind": "ticket", "tag_color": "#ffc107", "tag_icon": "hourglass-half" } ]
}
]
}
ticket_id is the record's id, and what other endpoints take. ticket_prefix and ticket_number make the number people see, such as TCK-93.
Fields: ticket_id, ticket_prefix, ticket_number, ticket_source, ticket_category, ticket_subject, ticket_details, ticket_priority, ticket_status, ticket_sla_id, ticket_billable, ticket_schedule, ticket_onsite, ticket_vendor_ticket_number, ticket_feedback, ticket_url_key, ticket_created_at, ticket_updated_at, ticket_due_at, ticket_first_response_at, ticket_response_due_at, ticket_resolution_due_at, ticket_response_sla_met, ticket_resolution_sla_met, ticket_response_sla_alert_stage, ticket_resolution_sla_alert_stage, ticket_resolved_at, ticket_closed_at, ticket_archived_at, ticket_created_by, ticket_assigned_to, ticket_closed_by, ticket_vendor_id, ticket_client_id, ticket_contact_id, ticket_location_id, ticket_asset_id, ticket_quote_id, ticket_invoice_id, ticket_project_id, ticket_recurring_ticket_id, ticket_template_id, ticket_order, tags. With ticket_id, also its status's ticket_status_name, ticket_status_color, ticket_status_active, ticket_status_pauses_sla and ticket_status_order.
Create
| Field | Notes |
|---|---|
ticket_subject | Required |
client_id | The client. Leave it out for an internal ticket that isn't for any client. |
ticket_details | The description. HTML is kept. |
ticket_priority | Low, Medium, High or Urgent. Anything else is Medium. |
ticket_contact_id | Who it's for - one of the client's contacts. Leave it out for the client's primary contact. |
ticket_asset_id | One of the client's assets |
ticket_vendor_id, ticket_vendor_ticket_number | A vendor it's with - the client's or yours - and their reference for it |
ticket_assigned_to | An active agent's technician_id. An assigned ticket starts Open, and an unassigned one starts New. |
ticket_billable | 1 if the time on it is billable |
ticket_template_id | Raise it from this ticket template. See From a template. |
ticket_fill_ins | The template's fill-ins answered, as an object: {"Employee Name": "Sam Reyes", "Needs a Computer": "Yes"} |
ticket_status | An open status to start in, by number - 3 On Hold, say. Not Resolved or Closed. Otherwise it starts New, or Open when assigned. |
ticket_invoice_id | One of the client's invoices it waits on. When that's paid in full, the ticket gets a note, goes from On Hold back to Open, and its agent is told. |
ticket_notify | 1 to tell people about it as when it's raised in ITFlow, or agents to tell only your team. See below. |
A contact, asset or vendor that isn't the client's is left off. The ticket gets its number and SLA as any other ticket does, and its source is API.
No email goes out unless you send ticket_notify. With 1, the contact and the ticket's watchers get the Ticket Created email (when Email clients about their tickets is on, in Admin > Settings > Notifications), the new-ticket address hears about it, and so does the agent it's assigned to. With agents, only the new-ticket address and the agent do - for a ticket the client already has an email about, such as its invoice.
curl -X POST https://itflow.example.com/api/v1/tickets/create.php \
-H "Content-Type: application/json" \
-d '{"api_key": "YOUR_API_KEY", "client_id": 1, "ticket_subject": "Backup job failed on RDG-SRV-01", "ticket_details": "Veeam reported the 01:00 job failed: repository unreachable.", "ticket_priority": "High", "ticket_asset_id": 8}'
{ "success": "True", "count": "1", "data": [ { "insert_id": 1880 } ], "ticket_reference": "TCK-1880", "ticket_url": "https://itflow.example.com/guest/guest_view_ticket.php?ticket_id=1880&url_key=..." }
ticket_reference is the number people see. ticket_url is the link its contact opens it with - the one in its emails - so a form on your website can link to it.
From a template
With ticket_template_id, the ticket gets what the template sets up: its subject and details unless you send your own, its category, priority, billing and agent unless you send them, and its tags, due time and tasks. The tasks that are only for some answers come along when the answers in ticket_fill_ins call for them, and the answers fill in the template's square-bracket words in the subject, details and tasks. They stay on the ticket, where canned responses fill them in too.
{
"api_key": "YOUR_API_KEY",
"client_id": 1,
"ticket_subject": "Hosted ITFlow: Ravenwood Dental - ravenwood.itflow.app",
"ticket_details": "<p>Signed up on our website.</p>",
"ticket_template_id": 12,
"ticket_fill_ins": { "Hostname": "ravenwood.itflow.app", "Address": "Our domain", "Technicians": "4" },
"ticket_notify": 1
}
The answer has ticket_tasks too, the number of tasks it got. A template that isn't there or is archived, a required fill-in left out, a choice that isn't one of a list's, or ticket_fill_ins that isn't an object is refused with 400 and why in message:
{ "success": "False", "message": "Fill in Hostname - it is needed for this template." }
A status or invoice that won't do is refused the same way.
The template's id is the number in its address in Admin > Ticket Templates. Fill-ins are matched by name, whatever the case; ones the template doesn't have are left out. A Yes or no fill-in takes Yes or No, and a Date one 2026-11-05.
Update
Send ticket_id, client_id (the ticket's own client), and any of the fields under Create apart from client_id. Only what you send changes, and a closed ticket can't be changed. To change a ticket's status, add a reply with ticket_status, or resolve or close it.
{ "api_key": "YOUR_API_KEY", "client_id": 1, "ticket_id": 1880, "ticket_assigned_to": 2, "ticket_priority": "Urgent" }
Resolve and close
Both take ticket_id and client_id, the ticket's own client.
- Resolve sets the ticket to Resolved and records whether its resolution SLA was met. A ticket that hadn't had a first response gets one now.
- Close resolves the ticket if it isn't already, then closes it, as the key's agent. A closed ticket can't be changed any more.
To say why when you resolve, add a reply with "ticket_status": 4 instead. See Replies.
Ticket replies
| Endpoint | Does |
|---|---|
GET ticket_replies/read.php | Lists replies, or gets one |
POST ticket_replies/create.php | Adds a reply or note to a ticket |
Read
| Parameter | Does |
|---|---|
ticket_reply_id | Just this reply |
ticket_id | Just this ticket's replies, oldest first |
type | Internal (notes), Public (replies to the client) or Client (the client's own replies) |
include_archived | 1 to include archived replies |
client_id | Just replies on this client's tickets |
limit, offset | Paging |
{
"success": "True",
"count": 1,
"data": [
{
"ticket_reply_id": "2553",
"ticket_reply": "Repository share was offline after a NAS update. Remounted and re-ran the job - it completed.",
"ticket_reply_type": "Internal",
"ticket_reply_emailed": "0",
"ticket_reply_emailed_to": null,
"ticket_reply_time_worked": "00:25:00",
"ticket_reply_created_at": "2026-10-22 00:37:54",
"ticket_reply_by": "1",
"ticket_reply_by_name": "Alex Morgan",
"ticket_reply_ticket_id": "1880",
"ticket_prefix": "TCK-",
"ticket_number": "1868",
"ticket_subject": "Backup job failed on RDG-SRV-01",
"ticket_client_id": "1"
}
]
}
ticket_reply_by is an agent's id on Internal and Public replies, and a contact's id on Client replies. ticket_reply_by_name is their name either way.
ticket_reply_emailed is 1 when a Public reply was emailed and 0 when one of your replies wasn't. It's null on a Client reply, and on one from before ITFlow kept this. ticket_reply_emailed_to is who it went to, as a JSON list - [{"email": "praghavan@ravenwooddental.example", "name": "Priya Raghavan"}], with an empty name for a watcher.
Fields: ticket_reply_id, ticket_reply, ticket_reply_type, ticket_reply_emailed, ticket_reply_emailed_to, ticket_reply_time_worked, ticket_reply_created_at, ticket_reply_updated_at, ticket_reply_archived_at, ticket_reply_by, ticket_reply_by_name, ticket_reply_ticket_id, and the ticket's ticket_prefix, ticket_number, ticket_subject and ticket_client_id.
Create
| Field | Notes |
|---|---|
ticket_id | Required |
ticket_reply | Required. HTML is kept. |
ticket_reply_type | Internal, a note only your team sees - the default - or Public, which emails the contact and anyone watching, and counts as the first response |
ticket_reply_time_worked | Time to log, as HH:MM:SS. None if you leave it out. |
ticket_status | Also set the ticket's status, by number. 4 resolves it. |
client_id | The ticket's client. Needed when the key's agent can see only some clients. |
The reply is from the key's agent.
curl -X POST https://itflow.example.com/api/v1/ticket_replies/create.php \
-H "Content-Type: application/json" \
-d '{"api_key": "YOUR_API_KEY", "ticket_id": 1880, "ticket_reply": "Repository share was offline after a NAS update. Remounted and re-ran the job - it completed.", "ticket_reply_time_worked": "00:25:00", "ticket_status": 4}'
Technicians
| Endpoint | Does |
|---|---|
GET technicians/read.php | Lists your agents, with their role |
GET technicians/time.php | Time worked, by ticket and technician |
Read
| Parameter | Does |
|---|---|
technician_id | Just this agent |
archived | 1 to include archived agents |
limit, offset | Paging |
{
"success": "True",
"count": 1,
"data": [
{ "technician_id": "2", "technician_name": "Jordan Lee", "technician_email": "jordan@summitit.example", "technician_status": "1", "role_id": "2", "role_name": "Technician", "role_is_admin": "0", "technician_created_at": "2026-10-21 19:41:45", "technician_archived_at": null }
]
}
technician_status is 1 for active and 0 for disabled. Passwords and sign-in details are never included. Use technician_id for ticket_assigned_to.
Time worked
| Parameter | Does |
|---|---|
year | The year. This year if you leave it out. |
month | 1 to 12. Every month of the year if you leave it out. |
technician_id | Just this agent's time |
client_id | Just time on this client's tickets |
limit, offset | Paging |
Each row is one technician's total on one ticket, from the time on their replies in that month or year. Tickets that aren't for a client are left out.
curl -G https://itflow.example.com/api/v1/technicians/time.php \
--data-urlencode "api_key=YOUR_API_KEY" \
--data-urlencode "year=2026" --data-urlencode "month=10"
{
"success": "True",
"count": 2,
"data": [
{ "ticket_id": "1863", "ticket_number": "TCK-1863", "ticket_subject": "Backup job reporting failures overnight", "client_id": "20", "company": "Ashford Legal Services", "technician_id": "1", "technician": "Alex Morgan", "time_worked": "01:25:00" },
{ "ticket_id": "820", "ticket_number": "TCK-820", "ticket_subject": "Outlook stuck on disconnected after password reset", "client_id": "8", "company": "Aurora Wellness Center", "technician_id": "1", "technician": "Alex Morgan", "time_worked": "00:30:00" }
]
}