Files

8.6 KiB
Raw Permalink Blame History

Ticket Scheduling Runbook (Schedule Entries)

End-to-end flow for "schedule my tickets on the calendar" requests, validated end-to-end on the VistaNet instance. All timestamps UTC.

1. Resolve the member

/system/myMember 404s for API members. Use GET /system/members?conditions=identifier='SBrown' (URL-encode quotes) → id. Reuse the id for every owner/id and member/id (SBrown = 147).

2. Pull open tickets

GET /service/tickets?conditions=owner/id=<memberId> AND closedFlag=false&pageSize=1000; filter client-side for truly-open (status starting >, name Closed, or dateResolved present ⇒ exclude). Handle both list and {"tickets": [...]} shapes.

3. Check SLA reality before doing SLA math

GET /service/SLAs/{id} tells you the plan exists. If /service/SLAs/{id}/plans returns EMPTY and /service/slaPlans returns 404, the SLA carries NO hour plans — Standard SLA (id 2) here has none, so every Priority 3–Medium ticket is SLA-identical and SLA contributes nothing to urgency. Do not invent response-time tiers from the SLA name. /service/boards/{id}/slaVersions and its /detail subpath also 404 on this instance — treat both as dead ends, not as a reason to stop ranking.

4. Rank by what changes a deadline

When priorities are uniform (all P3-Medium), rank from signals in the ticket data itself, in order:

  1. Hard expiration dates in the summary (cert renewals: "Expiring on YYYY-MM-DD") — expired or expiring this week first.
  2. Active security incidents and outages (summary words: intrusion, confirmed, outage, unreachable, failing) with status In Progress.
  3. Client-flagged urgency ("Urgent per ").
  4. Security hygiene (patching, key setup, directory sync, mail filtering).
  5. User-facing breakage (Tier 3 Assigned).
  6. Small tasks (new user setup) swept into slack blocks.

Do NOT cram project/coordination tickets (status Must Schedule) into gaps — they need client scheduling in long blocks; propose them for next week instead.

Unschedulable (list, don't book): On Hold, Waiting Client Response, Waiting parts/repair.

Status nuance: ticket status Scheduled only means SOME entry exists — often an all-day 0.0h placeholder months out. Check the entry's date against the summary expiry before trusting it.

5. Check existing commitments first

Fetch the week UNFILTERED — dateStart > [Monday-local ISO] AND dateStart < [NextMonday-local ISO], pageSize 500 — and filter by member.id/member.identifier client-side. Member-scoped conditions on this endpoint intermittently drop or mutate entries. Retry until two consecutive fetches agree (a stability gate), never gate on an absolute entry count — entries legitimately vanish mid-session when other people edit the calendar. Compose around firm entries; anchor back-to-back rather than overlapping (overlaps ⇒ 400 InvalidObject SystemConflict on dateStart). Note objectId-0 blocks (internal placeholders with no ticket behind them) and re-check commitments immediately before POSTing — the calendar can move between your fetch and your write.

6. Slot composition and dispositions

  • Local (PDT = UTC-7) to UTC conversion: +7h (2:30p PDT → 21:30Z).
  • Same-day urgent work goes TODAY in the first free block — an expiring cert waits for no one; start from the next half hour.
  • Batch small tasks (≤1h each) contiguously on the lightest afternoon.
  • Tickets whose start is gated externally (client confirmation pending, vendor scheduling unknown, still in evaluation) get NO entry — list them as dispositions with the blocking reason so nothing silently drops. Book when the gate clears. An ALREADY-BOOKED ticket whose gate moves later keeps its entry: reschedule via PATCH (§9), never delete+repost. A scheduling gate is recorded as an INTERNAL TICKET NOTE (§9), never in the entry name — a ticket-linked entry's display name auto-regenerates from Company/Summary and silently discards custom label text.
  • Recurring unavailability: team/tech meeting every Friday 8:00–9:30 AM PDT — never schedule SBrown in that window, and never let a Friday block start before 9:30.
  • After-hours work (evening server/RDS upgrades needing users logged off) gets a full evening block (e.g. 6–9p), not an afternoon gap fill, and needs a coordination runway — book the earliest evening the client could plausibly agree to, not tonight.
  • Booking requests are literal id lists: book exactly the named tickets; a duplicated id means one booking plus a note, not two entries.

7. POST each entry

POST /schedule/entries with a plain JSON object (NOT JSON Patch):

{
  "name": "Company / Ticket# 12345 - short label",
  "member": {"id": 147},
  "type":   {"id": 4},
  "span":   {"id": 1},
  "status": {"id": 2},
  "objectId": 12345,
  "ticketType": "S",
  "where": {"id": 4},
  "dateStart": "2026-10-06T16:00:00Z",
  "dateEnd":   "2026-10-06T18:00:00Z",
  "hours": 2.0,
  "doneFlag": false,
  "meetingFlag": false
}

Observed ids: span 1 = N, status 2 = Firm, type 4 = S, where 4 = Remote. name is the free-form calendar label.

8. Verify, then audit coverage

Re-GET the unfiltered week window and confirm each new entry by objectId AND intended start time; a 200 on the POST is not proof the calendar shows it. If a freshly posted entry is missing from the refetch, direct-GET the entry id: 404 ⇒ deleted/rolled back; present ⇒ transient list unreliability. Then audit the whole picture in the same pass: every open ticket with a summary expiration date vs its calendar coverage, every Must Schedule ticket vs entries, my timed entries pair-wise for overlaps, and next week's window for duplicate bookings of the same ticket. A vanished block usually means the ticket closed (>Completed), was reassigned (owner changed), or a duplicate was cleaned up — check ticket status/owner before treating absence as a scheduling failure.

Present results with local times, then a disposition list — booked / blocked-status (Waiting Client, On Hold, Waiting parts) / pending-external-gate / intentionally unbooked — plus flags: expired-but-waiting-on-client warnings, objectId-0 blocks with no ticket, and Must Schedule candidates for next week.

9. Modifying existing entries and recording gates

  • PATCH /schedule/entries/{id} requires a JSON Patch ARRAY (same as tickets): [{'op':'replace','path':'/dateStart','value':'...Z'},{'op':'replace','path':'/dateEnd','value':'...Z'},{'op':'replace','path':'/hours','value':3.0}]. A plain JSON object 400s. A 200 does NOT prove every field took — re-GET and check each patched field.
  • Do not patch /name on a ticket-linked entry: the write is accepted but the display name regenerates from Company/Summary, so any PENDING/DO-NOT-START marker in the label silently disappears. Skip it up front.
  • Scheduling gates live as internal ticket notes: POST /service/tickets/{id}/notes with a PLAIN JSON object (notes are not JSON Patch): {'text': '...', 'internalAnalysisFlag': true, 'processNotifications': false}. State the window, the entry id, who must confirm, and DO-NOT-START until then. customerFlag is not a valid ServiceNote member on this version (400 if included).
  • Owner reassignment (ticket leaves your queue): [{'op':'replace','path':'/owner/id','value':<memberId>}] on /service/tickets/{id}, then fresh-GET to verify owner identifier. Resolve the new member's id first with GET /system/members?conditions=identifier='X'. Remove the ticket from your scheduling plans the same turn — don't book/keep entries for tickets you no longer own.
  • Ownership hygiene check (standing rule): for a tech's open tickets whose most recent note is from another tech (a likely transfer), vet before PATCH: automation members (the API/service-account members) NEVER receive ownership — their notes are integration/alert noise; time entries count as "me being there" (older intake note beats nothing, loses to a newer TE by the current owner); read the note's intent — intake/directive notes ("please order and drop ship", "get you guys involved") keep the ticket with the current owner; when the current owner has NO note and NO TE and the other tech's note is newer, that's a legitimate transfer. Verify each PATCH with a fresh GET of owner.identifier; drop transferred tickets from any staged plan same turn.

Next-week candidate review

When asked what could be scheduled next week: fetch next + far-week windows (stability-gated), subtract every open ticket that has a booking in them, list the remainder grouped as schedulable (with concrete proposed slots from actual gaps), conditional-on-gate, and parked — naming the gate per ticket. Next-week Mon–Wed is typically wide open; propose day+time anchors, not vague availability.