bootsinthepark · everything up to 2026-08-03 · every figure double-checked
What this meansHow much higher the average happiness score is when AI was involved, against tickets staff handled alone. AI reads each conversation and scores it 1 to 5. Both numbers behind it are in the charts below. Rounding can shift the last decimal. Checked 2026-08-04T12:48:25Z.
Customers score conversations higher when AI is involved
Tickets AI was involved in average 3.49 out of 5. Staff-only tickets average 3.34. That is 4.3% higher. AI involved pools AI alone (3.5) and AI helped (3.44).
Out of 5: 3.5 for AI alone, 3.44 when AI helped, 3.34 for staff only.
64.1% of AI-only tickets read as sorted out, against 54.9% staff only and 54.6% when AI helped. 6,703 tickets have a score.
One entry per figure: what it means, the SQL that made it, and when it was last re-checked.
How much higher the average happiness score is when AI was involved, against tickets staff handled alone. AI reads each conversation and scores it 1 to 5
WITH s AS (
SELECT
CASE
WHEN t.imported THEN 'pre_easol'
WHEN t.ai_resolved THEN 'full_ai'
WHEN (t.ai_draft_sent_count > 0 OR t.booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN t.sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS bucket,
sen.inferred_satisfaction
FROM reporting.inbox_tickets t
JOIN inbox_ticket_sentiments sen ON sen.ticket_id = t.id
WHERE t.company_id = $1 AND t.created_at < $2
)
SELECT ROUND(100.0 * (
(SELECT AVG(inferred_satisfaction) FROM s WHERE bucket IN ('full_ai', 'partial_ai'))
/ (SELECT AVG(inferred_satisfaction) FROM s WHERE bucket = 'no_ai') - 1
), 1) AS value
Source: app/models/inbox/ticket_sentiment.rb:19 (validates :inferred_satisfaction); written at app/workers/inbox/analyze_sentiment_worker.rb:24
Verified: 2026-08-04T12:48:25Z
Tickets where AI has read the conversation and scored it
SELECT COUNT(*) AS value FROM reporting.inbox_tickets t JOIN inbox_ticket_sentiments s ON s.ticket_id = t.id WHERE t.company_id = $1 AND t.created_at < $2
Source: app/models/inbox/ticket.rb:27 (Inbox::Ticket has_one :sentiment, class_name: Inbox::TicketSentiment); app/models/inbox/ticket_sentiment.rb:8-9 (Inbox::TicketSentiment belongs_to :ticket, table inbox_ticket_sentiments)
Verified: 2026-08-04T12:48:25Z
The exact pair behind the headline — average score when AI was involved, against tickets staff handled alone
AI involved pools AI alone and AI helped, so it lands between those two, not on either
WITH s AS (
SELECT
CASE
WHEN t.imported THEN 'pre_easol'
WHEN t.ai_resolved THEN 'full_ai'
WHEN (t.ai_draft_sent_count > 0 OR t.booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN t.sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS bucket,
sen.inferred_satisfaction
FROM reporting.inbox_tickets t
JOIN inbox_ticket_sentiments sen ON sen.ticket_id = t.id
WHERE t.company_id = $1 AND t.created_at < $2
)
SELECT
CASE WHEN bucket = 'no_ai' THEN 'no_ai' ELSE 'ai_touched' END AS label,
ROUND(AVG(inferred_satisfaction)::numeric, 2) AS value
FROM s
WHERE bucket IN ('full_ai', 'partial_ai', 'no_ai')
GROUP BY 1 ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb
Verified: 2026-08-04T12:48:25Z
AI reads each conversation and scores how happy the customer seems, 1 to 5. Averaged by who handled the ticket
SELECT
CASE
WHEN t.imported THEN 'pre_easol'
WHEN t.ai_resolved THEN 'full_ai'
WHEN (t.ai_draft_sent_count > 0 OR t.booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN t.sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS label,
ROUND(AVG(s.inferred_satisfaction)::numeric, 2) AS value
FROM reporting.inbox_tickets t
JOIN inbox_ticket_sentiments s ON s.ticket_id = t.id
WHERE t.company_id = $1 AND t.created_at < $2
GROUP BY 1 ORDER BY 1
Source: app/models/inbox/ticket_sentiment.rb:19 (Inbox::TicketSentiment validates :inferred_satisfaction); written at app/workers/inbox/analyze_sentiment_worker.rb:24
Verified: 2026-08-04T12:48:25Z
Of scored tickets, the share where the conversation reads as sorted out, split by who handled them
SELECT
CASE
WHEN t.imported THEN 'pre_easol'
WHEN t.ai_resolved THEN 'full_ai'
WHEN (t.ai_draft_sent_count > 0 OR t.booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN t.sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS label,
ROUND(100.0 * COUNT(*) FILTER (WHERE s.resolution_status = 'resolved') / COUNT(*), 1) AS value
FROM reporting.inbox_tickets t
JOIN inbox_ticket_sentiments s ON s.ticket_id = t.id
WHERE t.company_id = $1 AND t.created_at < $2
GROUP BY 1 ORDER BY 1
Source: app/models/inbox/ticket_sentiment.rb:11-12 (Inbox::TicketSentiment enum :resolution_status); written at app/workers/inbox/analyze_sentiment_worker.rb:25
Verified: 2026-08-04T12:48:25Z
How many tickets each group has a mood score for. Every happiness figure is a share of these
WITH s AS (
SELECT
CASE
WHEN t.imported THEN 'pre_easol'
WHEN t.ai_resolved THEN 'full_ai'
WHEN (t.ai_draft_sent_count > 0 OR t.booking_action_performed_by_ai_count > 0) THEN 'partial_ai'
WHEN t.sent_message_count > 0 THEN 'no_ai'
ELSE 'no_reply'
END AS bucket,
sen.inferred_satisfaction
FROM reporting.inbox_tickets t
JOIN inbox_ticket_sentiments sen ON sen.ticket_id = t.id
WHERE t.company_id = $1 AND t.created_at < $2
)
SELECT bucket AS label, COUNT(*) AS value
FROM s
GROUP BY 1 ORDER BY 1
Source: app/models/reporting/inbox/ticket.rb
Verified: 2026-08-04T12:48:25Z