Inspiration

Every managed-services team lives with a split brain: the SLA contract says what was promised, and the helpdesk (Freshservice, in our case) says what's actually happening — and nobody is continuously checking whether the two still agree. Breaches get discovered after the deadline passes, usually from an angry customer email, not from the tooling that was supposed to catch it. We wanted a system that reads the actual contract PDF, understands its promises as structured rules, and then watches live tickets against those rules in real time — surfacing risk before the clock runs out, not after.

What it does

Holdline connects two sources of truth that normally live apart:

  • Contract intake: upload an SLA contract PDF, and Gemini extracts draft SLA rules (response/resolution targets by priority, service, customer) for a human to review and approve — nothing goes live without sign-off.
  • Live ticket sync: a background poller (and a webhook accelerator) pulls tickets from Freshservice continuously, matching each one against approved SLA rules by customer, service, priority, and ticket type.
  • Deterministic countdowns: every matched ticket gets a live SLA countdown — deadline, time remaining, elapsed time, status — recomputed every 30 seconds, no LLM involved in the math.
  • Breach-risk detection: tickets trending toward breach get flagged with a cached, LLM-generated plain-English explanation and a recommended action.
  • Human-gated intervention: when a ticket is at risk or breached, an authenticated SLA Manager can approve one of four intervention actions directly from the alert panel or ticket detail page. Freshservice access is read-only by default — there is exactly one write path in the whole codebase, it always re-verifies the write landed, and it always requires explicit human approval. There is no autonomous path to Freshservice anywhere.
  • Needs Review queue: any ticket that doesn't cleanly match exactly one approved rule (zero matches, or an ambiguous multi-match) lands in a Needs Review queue instead of silently guessing.

How we built it

  • Backend: FastAPI + Postgres (via psycopg), with apscheduler running the 30-second Freshservice sync loop alongside a webhook endpoint for near-instant updates on ticket changes.
  • Contract extraction: PDFs are parsed with pypdf and sent to Gemini (google-genai) to produce structured draft SLA rules; extraction and explanation calls are wrapped in retry logic (tenacity) to survive transient Gemini 503s.
  • Matching engine: a strict, deterministic matcher (no LLM) — exact-field matching against approved rules only, so match decisions are auditable and reproducible.
  • Frontend: a lightweight React + Vite SPA that surfaces the dashboard, ticket queues, contract upload flow, and the intervention/alert panel.
  • Auth & sessions: bcrypt + itsdangerous for password hashing and signed session cookies, keeping the SLA Manager role gated behind real authentication.
  • Deployment: containerized with Docker Compose for local dev; the frontend deploys to Vercel and the backend to Render (with an interactive wizard script to walk through first-time deploy setup).

Challenges we ran into

  • Keeping the LLM out of the critical path. It was tempting to let Gemini "decide" if a ticket breaches its SLA, but that's non-deterministic and unauditable. We split the system so LLM calls only touch extraction (contract → draft rules) and explanation (why is this ticket at risk, in plain English) — the actual countdown and breach math is pure, deterministic arithmetic.
  • Silent failure modes in the sync pipeline. Freshservice payloads that were missing expected fields, or an unreachable API, could silently produce zero synced tickets with no visible error in the UI — we had to add explicit logging and one-time warnings so these failures are debuggable instead of invisible.
  • Never letting automation touch the customer's helpdesk unsupervised. Every intervention has to be a real human's real click, re-verified after execution, with a full audit trail — even though "just auto-resolve it" would have been the easier feature to build.
  • Gemini API flakiness. Transient 503s during extraction/explanation calls needed real retry handling rather than surfacing raw failures to users.

What we learned

That the most valuable thing an "AI" product can do here isn't generating text — it's disciplined plumbing: strict matching, deterministic countdowns, and a hard human-approval gate around the one dangerous action (writing back to Freshservice). The LLM is useful exactly where judgment/summarization is needed, and useless (even risky) where determinism and auditability matter more.

What's next for Holdline

  • The Agent role's own login (currently only SLA Manager auth is built).
  • Seeding a real Freshservice trial account with live agents, groups, and sample tickets to demo against production data instead of local fixtures.
  • Broader SLA rule types beyond the current priority/service/customer matching dimensions.

Built With

Share this project:

Updates