Skip to content

REST API Changes

Ed Mozley edited this page Jul 2, 2026 · 1 revision

🟠 REST API: Changes

The complete usage guide for the Change Management module of the REST API β€” every URL, method, parameter and response shape, including the full CAB approval workflow. Mirrors the interactive documentation at System β†’ API β†’ Documentation (with its live "Try it" tester).

Note

New to the API? The basics table on REST API: Tickets covers base URL, authentication, the response envelope, error codes, rate limits and PATCH semantics β€” identical across all modules.

What's distinctive about Changes:

🌍 Install-wide Changes have no company β€” a key's company scope does not restrict them (matches the UI).
🎲 Risk is computed Send risk_likelihood and risk_impact_score (1–5 each); the API computes score = likelihood Γ— impact and bands the level exactly like the UI: ≀4 Low Β· ≀9 Medium Β· ≀15 High Β· ≀20 Very High Β· else Critical.
πŸ—³οΈ CAB voting works over the API The key's acts-as analyst can vote, and the auto-transition mechanics are the UI's own: any required Reject sends the change back to Draft; the all/majority threshold of required members flips it to Approved.
🧾 Strict where it matters Unknown status/type/priority/impact names are a 422 (the UI silently falls back to defaults β€” machines deserve an error).

πŸš€ Quick start

Raise a change, staff its CAB, submit it, and approve it β€” entirely over the API:

B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_…"

# 1. Create (Draft by default; risk computed server-side)
CHG=$(curl -s -X POST "$B/changes" -H "$K" -H "Content-Type: application/json" -d '{
  "title": "Replace SFP on core switch uplink",
  "change_type": "Normal", "priority": "High",
  "reason_for_change": "Fixes PRB-00005 (recurring VPN drops).",
  "risk_likelihood": 2, "risk_impact_score": 4,
  "cab_required": true, "cab_approval_type": "all",
  "work_start_at": "2026-07-05T06:00:00Z", "work_end_at": "2026-07-05T08:00:00Z"
}' | jq -r '.data.id')

# 2. Staff the CAB
curl -s -X POST "$B/changes/$CHG/cab" -H "$K" -H "Content-Type: application/json" \
  -d '{"members": [{"analyst_id": 1, "is_required": true}, {"analyst_id": 2, "is_required": false}]}'

# 3. Submit for approval
curl -s -X PATCH "$B/changes/$CHG" -H "$K" -H "Content-Type: application/json" -d '{"status": "Pending Approval"}'

# 4. A CAB member's key casts the deciding vote β†’ auto-Approved, workflow fires
curl -s -X POST "$B/changes/$CHG/cab/vote" -H "$K" -H "Content-Type: application/json" \
  -d '{"vote": "Approve", "comment": "Rollback plan looks solid."}'

Every step lands in the change's audit trail exactly as if it happened in the UI. 🧾


🟠 Changes

🟒 GET /changes β€” list / search Β  πŸ”‘ changes.read

Parameter Description
state open | closed | all (default all) β€” closed = Rejected/Completed/Failed/Cancelled
status / status_id e.g. Pending Approval, by name or id
change_type / change_type_id Standard / Normal / Emergency
priority / priority_id Β· impact / impact_id By name or id
category_id Β· requester_id Β· assigned_to_id Β· approver_id More id filters
cab_required true / false
risk_level Low, Medium, High, Very High, Critical
q Search title β€” CHG-0042 (or chg42) finds a change by its reference
work_start_from / work_start_to πŸ“… Scheduled-work window (ISO 8601) β€” the calendar shape
created_since / modified_since ISO 8601 date-time bounds
sort created_at (default, desc), modified_at, id, title, work_start_at, risk_score, priority, status β€” prefix - for descending
page / per_page Pagination β€” default 1 / 25, max 100

The change shape (lists; detail adds bodies, PIR, attachments, linked problems):

{ "id": 11, "change_number": "CHG-0011",
  "title": "Replace SFP on core switch uplink",
  "change_type": { "id": 2, "name": "Normal" },
  "status":      { "id": 4, "name": "Approved", "is_closed": false },
  "priority":    { "id": 3, "name": "High" },
  "impact":      { "id": 2, "name": "Medium" },
  "category":    null,
  "requester": null, "assigned_to": { "id": 1, "name": "Administrator" }, "approver": null,
  "approval_at": "2026-07-02T23:30:32Z",
  "cab":  { "required": true, "approval_type": "all" },
  "risk": { "likelihood": 2, "impact": 4, "score": 8, "level": "Medium" },
  "schedule": { "work_start_at": "2026-07-05T06:00:00Z", "work_end_at": "2026-07-05T08:00:00Z",
                "outage_start_at": null, "outage_end_at": null },
  "created_by": { "id": 1, "name": "Administrator" },
  "created_at": "2026-07-02T23:30:12Z", "modified_at": "2026-07-02T23:30:32Z" }

πŸ”΅ POST /changes β€” create Β  πŸ”‘ changes.create

Only title is required. Omitted lookups get the module defaults (Normal / Draft / Medium / Medium); a creation entry is written to the audit trail. Returns 201.

Field group Fields
Lookups (name or id) change_type Β· status Β· priority Β· impact Β· category_id
People (analyst ids) requester_id Β· assigned_to_id Β· approver_id
Schedule (ISO 8601) work_start_at Β· work_end_at Β· outage_start_at Β· outage_end_at
Plans (long text) description Β· reason_for_change Β· risk_evaluation Β· test_plan Β· rollback_plan
Risk (1–5 each) risk_likelihood Β· risk_impact_score β†’ score + level computed
CAB cab_required (bool) Β· cab_approval_type (all | majority)

🟒 GET /changes/{id} β€” get one Β  πŸ”‘ changes.read

Everything above plus description, reason_for_change, risk.evaluation, test_plan, rollback_plan, the PIR block (review, was_successful, actual_start_at/end_at, lessons_learned, follow_up), the attachments list (name/size/type β€” files are managed in the UI), and linked_problems (problems this change fixes).

🟠 PATCH /changes/{id} β€” update Β  πŸ”‘ changes.update

Everything creatable is patchable, plus the PIR fields (pir_was_successful, pir_actual_start_at, pir_actual_end_at, pir_lessons_learned, pir_follow_up). Send only what changes; null clears; identical PATCHes are idempotent.

Audit parity with the UI: human field labels ("Title", "Status", "Work Start", …), display names for lookups, (empty) placeholders, status changes marked status_change β€” and the long plan bodies update silently (the UI doesn't audit them either). Changing risk inputs recomputes score + level. A genuine transition into Approved fires the change.approved workflow event.

Tip

The lifecycle is: Draft β†’ Pending Approval β†’ Approved β†’ Scheduled β†’ In Progress β†’ Completed / Failed (Rejected and Cancelled are terminal). Only the Pending Approval β†’ Approved/Draft leg is automated (by CAB votes); every other transition is an explicit status PATCH, exactly like the UI.

πŸ”΄ DELETE /changes/{id} β€” delete Β  πŸ”‘ changes.delete

Warning

Permanent. Deletes the change, removes its attachment files from disk, and cascades comments, CAB entries and the audit trail β€” same as the UI. No restore.


πŸ—³οΈ CAB (Change Advisory Board)

🟒 GET /changes/{id}/cab Β  πŸ”‘ change_cab.read

The roster with votes, plus live approval progress:

{ "data": { "cab_required": true, "approval_type": "all",
    "members": [ { "analyst_id": 1, "name": "Administrator", "is_required": true,
                   "vote": "Approve", "vote_comment": "Rollback plan looks solid.",
                   "voted_at": "2026-07-02T23:30:32Z" } ],
    "progress": { "required_total": 1, "required_approved": 1, "required_rejected": 0 } } }

πŸ”΅ POST /changes/{id}/cab β€” set the roster Β  πŸ”‘ change_cab.manage

Body: {"members": [{"analyst_id": 1, "is_required": true}, …]} β€” replaces the roster. Additions, removals and required↔optional switches are diffed and each writes its own audit entry (like the UI). Returns the updated roster.

πŸ”΅ POST /changes/{id}/cab/vote β€” cast a vote Β  πŸ”‘ change_cab.vote

Body: {"vote": "Approve" | "Reject" | "Abstain", "comment": "…"}. The vote is cast as the analyst the key acts as β€” they must be a CAB member who hasn't voted (403 if not a member, 409 if already voted).

The auto-transition is the UI's exact mechanics, and only runs while the change is in Pending Approval:

  • πŸ”΄ Any required member votes Reject β†’ the change goes back to Draft.
  • 🟒 The threshold of required members approve (all = everyone; majority = more than half) β†’ the change becomes Approved, approval_at is stamped, and the change.approved workflow event fires.
  • βšͺ Optional members' votes are recorded but don't count toward the tally; Abstain never transitions anything.

The response tells you what happened: {"vote": "Approve", "status_changed": true, "new_status": "Approved"}.


πŸ’¬ Comments & history

🟒 GET /changes/{id}/comments Β  πŸ”‘ change_comments.read

Comments newest first, with the author.

πŸ”΅ POST /changes/{id}/comments Β  πŸ”‘ change_comments.create

Body: {"text": "…"}. Always internal, attributed to the acts-as analyst, with a preview written to the audit trail. Comments can't be edited (parity with the UI) β€” only added and deleted.

πŸ”΄ DELETE /changes/{id}/comments/{comment_id} Β  πŸ”‘ change_comments.delete

Hard-deletes the comment.

🟒 GET /changes/{id}/audit Β  πŸ”‘ change_audit.read

Every audited event β€” field changes, status changes, CAB roster edits, votes, comment previews β€” with action (field_change / status_change / cab_vote / comment), old/new values and the analyst, newest first.


πŸ“š Reference data

Under the shared πŸ”‘ reference.read permission:

Endpoint Returns
🟒 GET /change-statuses Draft, Submitted, Pending Approval, Approved, Rejected, Scheduled, In Progress, Completed, Failed, Cancelled β€” with is_closed, is_default, colour
🟒 GET /change-types Standard, Normal, Emergency (plus custom)
🟒 GET /change-priorities Low, Medium, High, Critical
🟒 GET /change-impacts Low, Medium, High
🟒 GET /change-categories Your configured categories (empty until set up)

πŸ§ͺ Worked example: change calendar feed

A read-only key (changes: read) feeds an external calendar or maintenance-window banner:

B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_…"

# Everything scheduled in the next 14 days, soonest first
curl -s -H "$K" "$B/changes?work_start_from=$(date -u +%Y-%m-%dT%H:%M:%SZ)&work_start_to=$(date -u -d '+14 days' +%Y-%m-%dT%H:%M:%SZ)&sort=work_start_at&per_page=100" \
  | jq -r '.data[] | "\(.change_number)  \(.schedule.work_start_at)  [\(.risk.level // "-")]  \(.title)"'

And a pipeline check before deploys β€” is anything risky approved for tonight?

curl -s -H "$K" "$B/changes?status=Approved&risk_level=High" | jq '.meta.total'

Under the hood: REST API β€” How It Works Β· Other modules: Tickets Β· Assets Β· Problems Β· Keys & permissions: System β†’ API (System module) Β· Module docs: Change Management.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally