Service Desk SLA Triage
A worked example of an advanced workflow: a Claude-managed scheduled agent that, every hour, finds HaloPSA tickets that are breaching or at risk of breaching SLA (plus unassigned aging tickets), skips anything it already flagged this cycle, ranks the rest by urgency, and posts a prioritised triage digest to Slack so the service desk sees what needs attention before SLAs breach. There are no servers and no code to deploy: the agent is a saved prompt plus a schedule plus two MCP connectors, running in Claude’s cloud.
What it builds
Section titled “What it builds”The finished workflow runs unattended on an hourly cron. Each run it queries open HaloPSA tickets, classifies them as breached, at risk, or fine, skips any ticket it has already surfaced this cycle (using a per-ticket triage note as the guard), ranks the remainder by urgency, and delivers a canvas-backed digest to Slack — turning a reactive inbox into a proactive SLA watch. Writes are bounded: the agent adds a private triage note and posts to Slack; it never reassigns, closes, or changes a ticket’s status.
Prerequisites
Section titled “Prerequisites”Before building, your Claude account needs all of the following:
| Requirement | Notes |
|---|---|
| Conduit connector | Connected in claude.ai (https://conduit.wyre.ai/v1/mcp). Provides the halopsa-official__* tools. See Connecting an AI Client. |
HaloPSA connected in Conduit (halopsa-official) |
The HaloPSA API user behind the vendor connection must be able to read tickets and write ticket notes. This guide uses the halopsa-official integration (the first-party MCP) — not the separate halopsa integration. |
| Slack connector | The first-party Slack connector connected in claude.ai (https://mcp.slack.com/mcp). Provides slack_create_canvas and slack_send_message. |
| Scheduled routines access | Created via the /schedule capability; managed at claude.ai/code/routines. |
| A destination Slack channel | e.g. #support-sla, plus its channel ID. The Slack connector must have access to it. |
Known gotchas
Section titled “Known gotchas”These are the things that cost real time the first time through. Account for them up front and the build genuinely takes minutes.
- The idempotency guard is not optional for any hourly triage routine.
Without a per-ticket marker, every hourly run re-flags the same breaching
tickets and spams the channel with duplicates. The triage note (the cycle
marker
[sla-triage YYYY-MM-DD-HH]) is the guard: a rerun reads existing notes and skips any ticket already marked this cycle. This is the same idempotency move the Autotask Ticket Triage agent makes — the triage note here plays the same role that advancing a ticket to In Progress plays there. - Bounded writes — the routine should never make an irreversible
service-desk change. This agent writes a private triage note and posts
to Slack; it does not reassign tickets, change status, or alter priority.
Keep unattended routines bounded: a human reviews the digest and acts.
Authorising status or assignment changes in
permitted_toolswould let an hourly routine silently modify the queue without oversight. - The Conduit catalog has two HaloPSA integrations — use the right one’s
tools. Conduit exposes both a
halopsaintegration and ahalopsa-official(first-party MCP) integration, and every tool is namespaced by its vendor slug. This guide uses thehalopsa-official__*tools (halopsa-official__search_tickets,halopsa-official__get_one_ticket,halopsa-official__add_note_to_ticket). Listing thehalopsa__*tools instead will give you a different tool set and the routine will not work as written. permitted_toolsmust be populated per connector. A routine with a connector attached but an emptypermitted_toolslist runs with no tools and silently does nothing — no error, no output. List the exact tool names the routine needs.- A routine reaches only its attached connectors. The routine sandbox blocks arbitrary network egress, so notifications must go through the Slack connector’s tools, not an outbound webhook. Attach every connector the workflow touches.
- One-hour minimum cadence. Claude-managed routines reject any cron
faster than hourly —
*/15 * * * *fails outright. Cron runs in UTC: to fire at 09:00 America/New_York (UTC−4 in summer), use0 13 * * *. - If Slack doesn’t appear in the
/scheduleconnector list, read its details from an existing routine. Don’t conclude Slack is unsupported. Read itsconnector_uuidandurlfrom an existing routine that already uses Slack (RemoteTriggerlist → get →mcp_connections). - Prefer
halopsa-official__search_ticketsover per-record gets. The 60-second tool timeout makes a loop ofhalopsa-official__get_one_ticketcalls across a large queue risky. Pull as much as possible from the search payload and call the per-ticket get only when necessary detail is missing. If SLA fields are not surfaced by the search, usehalopsa-official__CF_Portal_SLA_Breachedorhalopsa-official__CF_Portal_Unassigned_Ticketsas supplementary discovery tools.
The one-shot build prompt
Section titled “The one-shot build prompt”With the connectors above in place, paste this to Claude. It confirms the Conduit connection, creates the routine, and verifies the idempotency guard end to end.
Build me a scheduled Service Desk SLA Triage agent using HaloPSA. Do all of this end to end:
1. Confirm the Conduit connector works and HaloPSA is reachable: call halopsa-official__search_tickets and verify it returns open tickets. Note: the Conduit catalog has two HaloPSA integrations (halopsa and halopsa-official). This routine uses the halopsa-official__* tools — the first-party MCP — not the halopsa__* ones.2. Confirm a Slack connector is connected. Note the destination channel name and ID (e.g. #support-sla). If Slack does not show in the /schedule connector list, read its connector_uuid and url from an existing routine that already uses Slack (RemoteTrigger list -> get -> mcp_connections).3. Create a Claude-managed scheduled routine named "Service Desk SLA Triage": - Schedule: hourly (cron "0 * * * *"). Faster-than-hourly cadences are rejected. Cron runs in UTC — if your team works America/New_York, "0 13 * * *" is 09:00 ET, for example. - Attach TWO connectors, each with permitted_tools populated: * Conduit: halopsa-official__search_tickets, halopsa-official__get_one_ticket, halopsa-official__add_note_to_ticket * Slack: slack_create_canvas, slack_send_message An empty permitted_tools list = the routine runs with no tools. - Routine logic (see the routine prompt below): every run, find open tickets that are breaching SLA or within the risk threshold; skip any ticket that already has this cycle's triage note (the idempotency guard); rank the rest by urgency; add the triage-note guard to each newly surfaced ticket; build a prioritised digest; post a Slack canvas with the full triage list and a one-line summary linking it. Do NOT change ticket status or assignment — writes are bounded to the triage note and Slack only.4. Trigger a manual run and verify: a canvas was created, the triage note was added to each flagged ticket, a one-line summary landed in the destination channel, and triggering a second run immediately does NOT re-flag the same tickets (the note guard prevents it).
Alternative discovery: if halopsa-official__search_tickets does not surfaceSLA-breaching tickets directly, the halopsa-official integration also exposesCF_Portal_SLA_Breached and CF_Portal_Unassigned_Tickets as report-style tools— use those as a supplementary discovery path if needed.The resulting routine prompt
Section titled “The resulting routine prompt”This is the lean prompt the build process installs into the scheduled routine itself. Substitute your destination Slack channel name and ID.
You are the Service Desk SLA Triage agent. You run once per hour. Keep it lean.
Use ONLY: halopsa-official__search_tickets, halopsa-official__get_one_ticket,halopsa-official__add_note_to_ticket, slack_create_canvas, slack_send_message.
Definitions:- BREACHED: ticket's SLA response or resolution deadline has already passed.- AT RISK: ticket is within 60 minutes of its SLA deadline (or unassigned and open for more than 2 hours — these are at risk by default).- CYCLE MARKER: a triage note whose text starts with "[sla-triage YYYY-MM-DD-HH]" where YYYY-MM-DD-HH is the current UTC date and hour (e.g. [sla-triage 2026-05-27-14]). A ticket that already carries this exact marker was surfaced in this cycle; skip it — do not re-flag, do not post it again.
Steps:1. halopsa-official__search_tickets: retrieve all open tickets. Request enough fields to determine SLA status and whether a cycle marker note already exists for this hour.2. For each open ticket: a. Classify as BREACHED, AT RISK, or neither. Tickets that are neither — skip. b. Check for a cycle marker note matching today's UTC date-hour. If present — skip. c. For tickets still in scope, call halopsa-official__get_one_ticket only if you need additional detail not present in the search payload (minimise per-record calls).3. For each newly surfaced ticket (not skipped): a. halopsa-official__add_note_to_ticket: post a private triage note. The note MUST begin with the cycle marker, e.g.: [sla-triage 2026-05-27-14] SLA triage: ticket flagged as BREACHED/AT RISK. No action required — this note is an automated idempotency marker. b. Record the ticket for the digest.4. Sort the digest: BREACHED tickets first (oldest deadline first), then AT RISK (soonest deadline first), then unassigned aging tickets.5. slack_create_canvas titled "SLA Triage - <today's date> <current UTC hour>:00 UTC" with the full ranked list: ticket number, subject, status, SLA deadline, classification (BREACHED / AT RISK / UNASSIGNED), and time remaining or overdue by.6. slack_send_message to #support-sla: a one-line summary ("SLA triage: N breached, M at risk — see canvas") linking the canvas.7. If no tickets are in scope after filtering, post nothing (do not send an empty canvas).8. If a ticket cannot be read or the triage note cannot be written, list it in a "could not check" section of the canvas instead of skipping it silently.
Bounded writes: add triage notes and post to Slack only. Do NOT change ticketstatus, priority, assignment, or any other ticket field.How it works
Section titled “How it works”Hourly cadence and why
Section titled “Hourly cadence and why”SLA deadlines are typically measured in hours, not days — a two-hour response target can breach between a weekly or even daily run. Hourly is the fastest cadence Claude-managed routines support, which makes it the natural fit for SLA watch. The idempotency guard (below) ensures that running hourly doesn’t flood the channel: a ticket is surfaced at most once per UTC hour, regardless of how many runs execute.
At-risk vs. breached classification
Section titled “At-risk vs. breached classification”The routine draws a line between two states. A ticket is breached when its SLA deadline has already passed; it is at risk when the deadline is within 60 minutes, or when it is unassigned and has been open for more than two hours. Everything else is skipped. The two-tier classification lets the digest lead with the most urgent items (breached, oldest first) and trail with the approaching ones (at risk, soonest deadline first), giving the service desk a clear action order at a glance.
The triage-note idempotency guard
Section titled “The triage-note idempotency guard”Before posting any ticket to Slack, the agent writes a private triage note
whose text begins with a stable, greppable marker:
[sla-triage YYYY-MM-DD-HH]. At the start of every run the agent reads
existing notes on each candidate ticket and skips any that already carry
this hour’s marker. The marker is the “already handled” signal — no
database, no timestamp window, no separate state store. This is the same
structural move the
Autotask Ticket Triage
agent makes by advancing a ticket to In Progress: both routines embed the
idempotency guard into the primary data source so reruns are
self-correcting.
Bounded writes — the human stays in control
Section titled “Bounded writes — the human stays in control”The only writes this routine makes are a private triage note (the guard) and a Slack message. It never changes a ticket’s status, priority, or assignment. That constraint is deliberate: an unattended hourly routine that can silently reassign or close tickets is a liability. The digest gives the service desk the full picture; remediation stays human. If a ticket cannot be read or the note cannot be written, the routine lists it in a “could not check” section of the canvas rather than skipping it silently. Every call the routine does make is recorded in Conduit’s audit log.
Canvas plus summary delivery
Section titled “Canvas plus summary delivery”The triage digest can span many tickets, so the routine publishes it as a Slack canvas — a durable, scrollable document — and posts only a one-line count summary to the channel linking it. The channel stays readable across multiple hourly runs; the detail lives in each canvas. See Delivery Adapters for other ways to surface a routine’s output.
Extending it
Section titled “Extending it”The routine is PSA-agnostic in structure. To run the same SLA triage against
Autotask, swap the halopsa-official__* tools
(halopsa-official__search_tickets, halopsa-official__get_one_ticket,
halopsa-official__add_note_to_ticket) for
autotask__autotask_search_tickets,
autotask__autotask_get_ticket_details, and
autotask__autotask_create_ticket_note — the idempotency pattern, the
at-risk classification, and the Slack delivery are identical. See the
Autotask Ticket Triage
guide for that setup. Alternatively, to escalate a breached ticket beyond a
Slack digest — for example, filing a GitHub or Linear issue through those
Conduit connections, or opening an internal escalation ticket — extend the
bounded-write step with the extra tools; the core classification and guard
logic does not change.
Questions or a workflow you’d like documented? See Getting support.

