-
Notifications
You must be signed in to change notification settings - Fork 15
REST API Maintaining the Catalogue
This is the developer guide for changing the REST API β adding an endpoint, a whole module, a parameter, or deprecating something β and keeping the interactive docs and the OpenAPI document correct with the least effort.
There is one file you edit: api/v1/spec.json. Everything else is generated from it.
| File | What it is | Do you edit it? |
|---|---|---|
api/v1/spec.json |
FreeITSM's own catalogue: every endpoint's summary, description, parameters, examples and errors | Yes β this is the source |
api/v1/lib/routes.php |
The route table the front controller dispatches | Yes, when adding/removing a route |
api/v1/lib/openapi_schemas.php |
Typed response schemas | No β a tool maintains it |
/api/v1/openapi.json |
The standard OpenAPI document | No β generated from the above |
system/api/docs.php |
The interactive docs page | No β it reads spec.json
|
Both the interactive docs page and the OpenAPI document read spec.json, so a single edit updates both. openapi.json is generated β never hand-edited (see the OpenAPI page for that distinction).
spec.json is { "spec": [ β¦sectionsβ¦ ], "extras": { β¦examples & errorsβ¦ } }.
A section groups endpoints under a heading:
{ "section": "Tickets", "items": [ β¦endpoint objectsβ¦ ] }An endpoint object in items:
{
"m": "POST", // GET | POST | PATCH | DELETE
"p": "/tickets/{id}/notes", // path; {id} placeholders become path params
"perm": "ticket_notes.create", // the permission it needs ("resource.action", or "none")
"s": "Add a note to a ticket", // one-line summary
"d": "Longer description of what it does and any quirks.",
"params": [ // path + query parameters (not body fields)
{ "name": "id", "in": "path", "desc": "Ticket id", "req": true },
{ "name": "internal", "in": "query", "desc": "true = internal note", "req": false }
],
"body": { "text": "Looks resolved on my end." } // example request body (POST/PATCH only)
}The matching extras entry (keyed "METHOD /path") holds the worked examples and endpoint-specific errors:
"POST /tickets/{id}/notes": {
"examples": [
{ "title": "Simplest", "note": "Just the text.", "path": { "id": 42 }, "body": { "text": "β¦" } }
],
"errors": [
{ "code": 409, "when": "The ticket is in the recycle bin." }
]
}Everything else β the security scheme, the {data, meta} envelope, the generic error responses (401/403/404/422/429), the success code, and the typed response schema β is added by the generator. You only supply what's specific to the endpoint.
- Add it to that endpoint's
paramsarray inspec.json:{ "name": "include_archived", "in": "query", "desc": "true = include archived", "req": false } - (Optional) add an example under the endpoint's
extrasthat uses it, so beginners see it in context. - Run the checks (below).
That's it β the docs page shows the new field and the OpenAPI document lists the query parameter, from the one edit.
-
Add the route to
api/v1/lib/routes.phpand write the handler in the module'sapi/v1/resources/*.php. Add a new permission toapi/v1/lib/permissions.phpif the action is new. -
Scaffold the catalogue entry β this reads the route table and prints a ready-to-paste stub for anything not yet documented:
Paste the stub into the right section's
php api/v1/dev/openapi_stub.phpitemsinspec.jsonand fill ins,d,params, an examplebody, and anyextras. - Type its response from a live call (see The verification loop).
- Run the checks.
The drift-guard (part of the checks) fails until the route has a catalogue entry, so you cannot forget step 2.
Same as adding an endpoint, at module scale, and it slots into the existing module recipe:
- Build the resource file, routes and permissions as for any module.
-
php api/v1/dev/openapi_stub.phpprints stubs for every new route β paste them under a new section inspec.json(give the section a heading, e.g."section": "Widgets"), and fill them in. Add a wiki usage page for the module if it warrants one. - Type the module's response schemas:
php api/v1/dev/openapi_fix.php <read_key>(re-run to0 patches). - Run the checks. The new section's endpoints appear in the docs nav and the OpenAPI
tagsautomatically.
The API keeps working, but the specification tells consumers to stop using it. Set "deprecated": true on the entry in spec.json:
{ "m": "GET", "p": "/old-thing", "perm": "β¦", "s": "β¦", "deprecated": true, "params": [] }or on a single parameter:
{ "name": "legacy_id", "in": "query", "desc": "Use object_id instead.", "req": false, "deprecated": true }The generated OpenAPI operation/parameter is marked deprecated: true, which Swagger UI, Redoc and Postman render struck through with a warning. Explain the replacement in the d (description) so consumers know what to move to. When you're ready to actually remove it, delete the route from routes.php and the entry from spec.json (the drift-guard confirms the two stay in step).
Note
Removing a field from a response is a breaking change even if the endpoint stays β drop it from the typed schema in openapi_schemas.php (re-running openapi_fix.php against an install that no longer returns it will not re-add it) and call it out in the endpoint's d.
After any change, from the app root (needs only PHP):
# 1. bring typed response schemas in line with live data (needs a read key)
php api/v1/dev/openapi_fix.php <read_key> # re-run until "0 patches"
# 2. confirm every schema matches a real response
php api/v1/dev/openapi_verify.php <read_key> # aim for "0 failed"
# 3. confirm the invariants (drift, refs, operationIds, responses, shape, nullable, catalogue)
php api/v1/lib/openapi_check.php # must print "PASS"
# 4. confirm conformance to the official OpenAPI 3.0 meta-schema
curl -s http://localhost/freeitsm-app/api/v1/openapi.json > /tmp/o.json
php api/v1/dev/jsonschema4.php /tmp/o.json api/v1/dev/oas-3.0-schema.jsonCreate the read key under System β API with the permissions you're touching, and delete it afterwards. api/v1/dev/README.md has the same workflow next to the tools.
openapi_check.php (step 3) is the safety net β it exits non-zero on any of:
- an endpoint in
routes.phpwith nospec.jsonentry, or vice versa (drift); - a
spec.jsonentry that's malformed β bad method, missing summary, a parameter with noin(catalogue); - an
extraskey that doesn't match a real endpoint; - a response schema referencing a component that doesn't exist (refs);
- duplicate operation ids, an operation with no responses, or the subtler OpenAPI traps (empty-array-as-object,
nullablewithouttype).
So the honest summary: edit spec.json, run the loop, and the checks tell you if anything is inconsistent β you don't have to hold the whole system in your head.
See also: REST API: OpenAPI specification Β· how it's kept correct Β· the interactive docs page.
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
- β³ π 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)