Skip to content

First response time (median)

How long does a customer wait before a human acknowledges the ticket?

Current value (service case data): 0.9 h - ✓ On target. Open the case.

Field Value
Definition Median elapsed hours between ticket creation and the first agent response, over tickets that have received a response.
Formula median(first_response_at - created_at) [hours]
Unit hours
Granularity Ticket; reported weekly by priority and queue.
Direction Lower is better ↓
Target and thresholds ≤ 2.0 h (amber ≤ 4.0 h)
Parameters –
Owner Service Desk Lead
Source Ticketing system (synthetic generator)
Input table tickets, columns first_response_hours
Recommended chart line - Trend over weeks against the target line; the question is 'are we drifting?'.
Related KPIs Resolution time (median), SLA compliance

How to read it

Half of the tickets got a first answer faster than this value. Read it per priority: a good fleet median can hide P1 tickets waiting overnight.

Common misreadings

  • Using the mean - a handful of tickets answered after a weekend dominates it.
  • Counting auto-replies as a first response makes the KPI look perfect and meaningless.
  • Ignoring unanswered tickets - they are censored, not fast; track them as backlog.

Worked example (from the test suite)

ticket_id status first_response_hours resolution_hours sla_target_hours contacts reopened age_days
1 resolved 1.0 10.0 24.0 1 False –
2 resolved 3.0 30.0 24.0 2 False –
3 resolved 0.5 4.0 8.0 1 True –
4 resolved 6.0 50.0 72.0 1 False –
5 open – – 24.0 1 False 9.0

Expected result: 2.00 h. Responded tickets: 0.5, 1, 3, 6 h (ticket 5 has no response yet). Median of 4 values = (1 + 3) / 2 = 2.0 h.

This example is asserted in tests/test_formulas.py against the Python function and in tests/test_sql.py against the SQL below.

Implementations

Python function (the single implementation used by the site):

@kpi("first_response_time")
def first_response_time(df: pd.DataFrame) -> float:
    return median_or_nan(df["first_response_hours"])

Reference SQL (tested on SQLite):

WITH x AS (
  SELECT first_response_hours AS v,
         ROW_NUMBER() OVER (ORDER BY first_response_hours) AS rn,
         COUNT(*) OVER () AS n
  FROM tickets WHERE first_response_hours IS NOT NULL)
SELECT AVG(v) FROM x WHERE rn IN ((n + 1) / 2, (n + 2) / 2)