bootsinthepark · everything up to 2026-08-03 · every figure double-checked
What this meansCut in the typical wait for a first reply — AI alone now, against the old email threads from before Easol. Measured the same way on both sides: customer's first message to the first reply out. Against Easol tickets staff handled alone, AI is a shade slower, not faster. Both numbers behind it are in the charts below. Rounding can shift the last decimal. Checked 2026-08-03T16:10:59Z.
Customers get a first reply in hours, not days
Half of AI-only tickets get a first reply within 7.6 hours. Before Easol the middle ticket waited 100.5 hours. Same day instead of four days later. That is 92.4% off.
First reply is timed the same way on both sides: customer's first message to the first reply out. Time to close is not, which is why 8.3 hours against 144.6 needs more caveats than this number does.
On Easol the three groups are close, and AI alone is a shade slower: 7.6 hours, against 6.8 when AI helped and 6.6 for staff only. 3,835 AI-only tickets and 563 from before Easol got a reply.
The weekly lines are not flat. AI-only first replies sat between 1.6 and 9.7 hours from late February to late April, then peaked at 144.6 hours in the week of 22 June, with time to close peaking at 166.0 hours the same week. Staff-only tickets peaked too, at 81.3 hours in the week of 6 July. Festival season filled the inbox; this is not an AI problem. Even the worst week sits inside the 97.6 to 170.2 hour range that was normal before Easol.
Middle time each week, by the week a ticket came in. Quiet weeks (under 10 tickets) are left out. Before and after cover different months, so they are two charts on two scales, not one line.
Middle time each week, by the week a ticket came in. Quiet weeks (under 10 tickets) are left out. Before and after cover different months, so they are two charts on two scales, not one line.
One entry per figure: what it means, the SQL that made it, and when it was last re-checked.
Cut in the typical wait for a first reply — AI alone now, against the old email threads from before Easol
Measured the same way on both sides: customer's first message to the first reply out. Against Easol tickets staff handled alone, AI is a shade slower, not faster
WITH r AS (
SELECT
CASE
WHEN imported THEN 'pre_easol'
WHEN ai_resolved THEN 'full_ai'
WHEN (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS bucket,
first_response_time_seconds
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
)
SELECT ROUND((100.0 * (1 -
(SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds) FROM r WHERE bucket = 'full_ai')
/ (SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds) FROM r WHERE bucket = 'pre_easol')
))::numeric, 1) AS value
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's wait from the customer's message to the first reply, split by who handled them
SELECT
CASE
WHEN imported THEN 'pre_easol'
WHEN ai_resolved THEN 'full_ai'
WHEN (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
GROUP BY 1 ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's time from first message to close, split by who handled them
Both clocks start at the first message. Easol tickets stop when marked resolved; old imported threads stop at the last email out, so a long email chain reads as one slow ticket
SELECT
CASE
WHEN imported THEN 'pre_easol'
WHEN ai_resolved THEN 'full_ai'
WHEN (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND resolution_time_seconds IS NOT NULL
GROUP BY 1 ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:148 (Reporting::Inbox::Ticket::AttributesBuilder#resolution_time_seconds)
Verified: 2026-08-03T16:10:59Z
Tickets with at least one reply sent, split by who handled them
SELECT
CASE
WHEN imported THEN 'pre_easol'
WHEN ai_resolved THEN 'full_ai'
WHEN (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS label,
COUNT(*) FILTER (WHERE sent_message_count > 0) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2
GROUP BY 1 ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:176 (Reporting::Inbox::Ticket#sent_message_count)
Verified: 2026-08-03T16:10:59Z
The middle ticket's first reply for each week's tickets, before easol only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
AND (imported)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's time to close for each week's tickets, before easol only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND resolution_time_seconds IS NOT NULL
AND (imported)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:148 (Reporting::Inbox::Ticket::AttributesBuilder#resolution_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's first reply for each week's tickets, ai alone only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
AND (NOT imported AND ai_resolved)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's time to close for each week's tickets, ai alone only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND resolution_time_seconds IS NOT NULL
AND (NOT imported AND ai_resolved)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:148 (Reporting::Inbox::Ticket::AttributesBuilder#resolution_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's first reply for each week's tickets, ai helped only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
AND (NOT imported AND NOT ai_resolved AND (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0))
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's time to close for each week's tickets, ai helped only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND resolution_time_seconds IS NOT NULL
AND (NOT imported AND NOT ai_resolved AND (ai_draft_sent_count > 0 OR booking_action_performed_by_ai_count > 0))
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:148 (Reporting::Inbox::Ticket::AttributesBuilder#resolution_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's first reply for each week's tickets, staff only only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY first_response_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND first_response_time_seconds IS NOT NULL
AND (NOT imported AND NOT ai_resolved AND ai_draft_sent_count = 0 AND booking_action_performed_by_ai_count = 0 AND sent_message_count > 0)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:142 (Reporting::Inbox::Ticket::AttributesBuilder#first_response_time_seconds)
Verified: 2026-08-03T16:10:59Z
The middle ticket's time to close for each week's tickets, staff only only. Weeks with under 10 tickets are left out so a handful cannot swing the line
Weeks are labelled by their Monday, and a ticket counts in the week it arrived
SELECT
DATE_TRUNC('week', created_at)::date::text AS label,
ROUND(((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY resolution_time_seconds))/3600.0)::numeric, 1) AS value
FROM reporting.inbox_tickets
WHERE company_id = $1 AND created_at < $2 AND resolution_time_seconds IS NOT NULL
AND (NOT imported AND NOT ai_resolved AND ai_draft_sent_count = 0 AND booking_action_performed_by_ai_count = 0 AND sent_message_count > 0)
GROUP BY 1
HAVING COUNT(*) >= 10
ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb:148 (Reporting::Inbox::Ticket::AttributesBuilder#resolution_time_seconds)
Verified: 2026-08-03T16:10:59Z