Files
freshservice-claude/reference.md
spencerm 0b6747812f Handle custom ticket statuses: list_ticket_statuses tool + unresolved shortcut
The pesco instance defines 13 ticket statuses, not the 4 defaults, so a
(status:2 OR status:3) 'open tickets' filter silently dropped Working,
Scheduled, Waiting*, Pending Approval, etc. — undercounting 24 open tickets as 1.

Add a status helper that reads the status field from /ticket_form_fields and
treats only ids 4/5 (and closed/resolved-labelled) as terminal. Expose
list_ticket_statuses() and a list_tickets(unresolved=True) shortcut that builds
the full non-terminal status clause automatically. Document the gotcha in the
skill reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:36:25 -06:00

97 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Freshservice API v2 reference
Base URL: `https://<domain>.freshservice.com/api/v2`. Auth is HTTP Basic with the
API key as username (handled by `scripts/fs.py`). Full docs: https://api.freshservice.com
## Codes
**Ticket status (defaults):** 2 Open · 3 Pending · 4 Resolved · 5 Closed
**Ticket priority:** 1 Low · 2 Medium · 3 High · 4 Urgent
> ⚠️ Instances often define **custom statuses** beyond the 4 defaults (e.g.
> "Working", "Scheduled", "Waiting Vendor Support"). Resolved and Closed are
> always ids **4** and **5**; every other status is "open". To list them all,
> read the `status` field choices:
> `GET /ticket_form_fields` → field where `name == "status"` → `choices[]`.
> So "all my tickets that aren't resolved/closed" must enumerate *every*
> non-4/5 status, not just `(status:2 OR status:3)`. Recipe:
> `agent_id:<id> AND (status:2 OR status:3 OR status:6 OR status:7 OR ...)`
> using all open ids from the status field.
**Ticket source:** 1 Email · 2 Portal · 3 Phone · 4 Chat · 5 Feedback widget ·
6 Yammer · 7 AWS CloudWatch · 8 PagerDuty · 9 Walkup · 10 Slack
Status/priority for changes use their own workflow codes; list a few existing
records to infer them, or read the ticket-fields / change-fields endpoints.
## Endpoints
| Resource | List | Create | View | Update | Delete |
|----------|------|--------|------|--------|--------|
| Tickets | `GET /tickets` | `POST /tickets` | `GET /tickets/{id}` | `PUT /tickets/{id}` | `DELETE /tickets/{id}` |
| Ticket filter | `GET /tickets/filter?query="..."` | — | — | — | — |
| Conversations | `GET /tickets/{id}/conversations` | reply: `POST /tickets/{id}/reply`; note: `POST /tickets/{id}/notes` | — | `PUT /conversations/{id}` | `DELETE /conversations/{id}` |
| Assets | `GET /assets` | `POST /assets` | `GET /assets/{display_id}` | `PUT /assets/{display_id}` | `DELETE /assets/{display_id}` |
| Agents | `GET /agents` | `POST /agents` | `GET /agents/{id}` | `PUT /agents/{id}` | — |
| Requesters | `GET /requesters` | `POST /requesters` | `GET /requesters/{id}` | `PUT /requesters/{id}` | — |
| Groups | `GET /groups` | `POST /groups` | `GET /groups/{id}` | `PUT /groups/{id}` | `DELETE /groups/{id}` |
| Departments | `GET /departments` | `POST /departments` | `GET /departments/{id}` | `PUT /departments/{id}` | `DELETE /departments/{id}` |
| Changes | `GET /changes` (+ `/changes/filter`) | `POST /changes` | `GET /changes/{id}` | `PUT /changes/{id}` | `DELETE /changes/{id}` |
| Problems | `GET /problems` | `POST /problems` | `GET /problems/{id}` | `PUT /problems/{id}` | `DELETE /problems/{id}` |
| Releases | `GET /releases` | `POST /releases` | `GET /releases/{id}` | `PUT /releases/{id}` | `DELETE /releases/{id}` |
| Current agent | `GET /agents/me` | — | — | — | — |
Other useful endpoints reachable the same way: `/solutions/categories`,
`/solutions/folders`, `/solutions/articles`, `/products`, `/vendors`,
`/ticket_fields`, `/tickets/{id}/time_entries`, `/canned_responses`.
## Ticket fields (create/update)
`subject`, `description` (HTML ok), `email` or `requester_id`, `priority`,
`status`, `source`, `group_id`, `responder_id` (assigned agent), `type`
(e.g. "Incident", "Service Request"), `tags` (array), `category`,
`sub_category`, `custom_fields` (object), `due_by`, `fr_due_by`.
Provide **either** `email` **or** `requester_id`. For a public reply the
requester is emailed; a note with `"private": true` is internal-only.
## Filter (query) syntax — /tickets/filter and /changes/filter
- Format: `field:value`, combined with `AND` / `OR`.
- Strings single-quoted: `tag:'vip'`. Numbers/dates bare or quoted.
- Operators on dates/numbers: `>`, `<`, e.g. `created_at:>'2026-01-01'`.
- Filterable ticket fields include: `status`, `priority`, `agent_id`,
`group_id`, `requester_id`, `type`, `source`, `tag`, `created_at`,
`updated_at`, `due_by`, plus custom fields by name.
- The whole expression must be URL-double-quoted; `scripts/fs.py` does this when
you pass `--param query=...` / `--param filter=...`.
Two common gotchas:
- **Assignee is `agent_id` (numeric), not an email.** Resolve an email to an id
first: `GET /agents?email=person@co.com` → read `id`, then filter on it.
- **No "not" operator for status.** "Open / unresolved / not closed" must be
enumerated: `(status:2 OR status:3)`. Parenthesize the OR group when AND-ing it.
Examples:
- `status:2 AND priority:3`
- `agent_id:0 AND status:2` — unassigned open tickets
- `agent_id:21000816864 AND (status:2 OR status:3)` — assigned to an agent, still open
- `type:'Incident' AND created_at:>'2026-06-01'`
Multi-workspace accounts: `/tickets/filter` searches one workspace by default.
Pass `--param workspace_id=<id>` to target another (an agent's `workspace_ids`
lists which ones they're in).
## Listing vs. filtering
- `GET /tickets` supports `filter` presets (`new_and_my_open`, `watching`,
`spam`, `deleted`), `updated_since`, `order_by`, `order_type`, `page`,
`per_page` (max 100), and `include` (`conversations`, `requester`, `stats`).
- `GET /tickets/filter?query="..."` is for ad-hoc field queries (also 100/page).
## Pagination & rate limits
- 30 results/page default, 100 max via `per_page`. `scripts/fs.py` surfaces
`next_page` when a next-page Link header is present.
- Per-plan minute limits (100500/min). Responses include
`rate_limit_remaining`.