Class: SmsRecipientDemoteHandler

Inherits:
ApplicationJob show all
Includes:
RailsEventStore::AsyncHandler
Defined in:
app/subscribers/sms_recipient_demote_handler.rb

Overview

Demotes a recipient's contact_point sms_status to sms_none when Twilio's
ErrorCode definitively classifies the recipient as not-SMS-capable
(landline, invalid mobile).

Logic Details

Only the recipient-side error allowlist below triggers a demote. Codes
meaning "our sender is unverified / unregistered / region-blocked" (e.g.
30032 Toll-Free Not Verified — the actual cause of the David Grégoire
case on 2026-05-06) are sender-side and have nothing to do with whether the
recipient can receive; demoting on those would silently disable conversations
every time the WY toll-free hit a new region. See
SmsSenderUnverifiedAlertHandler for the sender-side branch.

Iterates ALL matching contact points, not just sms_enabled ones, so a row
already at sms_none (default state, no Twilio confirmation) still gets its
sms_status_source upgraded to twilio_failure_<code> and stamped with a
sms_status_verified_at. Distinguishes "speculatively unverified" from
"carrier-confirmed not SMS-capable."

Constant Summary collapse

RECIPIENT_NOT_SMS_CODES =

Definitive recipient-side codes — one occurrence is enough to demote.

  • 30006 — Landline or unreachable carrier
  • 21614'To' number is not a valid mobile number
%w[30006 21614].freeze

Instance Method Summary collapse

Instance Method Details

#perform(event) ⇒ void

This method returns an undefined value.

Stamps the failure verdict onto every contact point whose detail matches
the failed recipient.

Parameters:

Raises:

  • (ActiveRecord::RecordInvalid)

    when a contact point fails to persist



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/subscribers/sms_recipient_demote_handler.rb', line 46

def perform(event)
  code = event.data[:error_code].to_s
  return unless RECIPIENT_NOT_SMS_CODES.include?(code)

  recipient = event.data[:recipient]
  return if recipient.blank?

  ContactPoint.dialable.where(detail: recipient).find_each do |cp|
    cp.update!(
      sms_status: :sms_none,
      sms_status_source: "twilio_failure_#{code}",
      sms_status_verified_at: Time.current
    )
  end
end