Add SMS START/re-subscribe handling to Twilio webhook

Customers who replied STOP by mistake can now reply START, YES, or
UNSTOP to automatically re-enable their SMS opt-in — no staff action
needed. Adds SmsInboundStart notification type, HandleStartAsync in
WebhooksController, and updates AI knowledge base and help docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-02 20:09:49 -04:00
parent 8148908a66
commit 3ff6a96bc8
4 changed files with 54 additions and 4 deletions
@@ -18,5 +18,6 @@ public enum NotificationType
SubscriptionExpired = 11,
SmsInboundStop = 12,
SmsInboundHelp = 13,
AdminEmail = 14
AdminEmail = 14,
SmsInboundStart = 15
}
@@ -31,6 +31,12 @@ public class WebhooksController : ControllerBase
"STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT"
};
// CTIA-standard opt-in keywords
private static readonly HashSet<string> StartKeywords = new(StringComparer.OrdinalIgnoreCase)
{
"START", "YES", "UNSTOP"
};
// CTIA-standard help keywords
private static readonly HashSet<string> HelpKeywords = new(StringComparer.OrdinalIgnoreCase)
{
@@ -77,6 +83,9 @@ public class WebhooksController : ControllerBase
if (StopKeywords.Contains(body))
return await HandleStopAsync(payload.From);
if (StartKeywords.Contains(body))
return await HandleStartAsync(payload.From);
if (HelpKeywords.Contains(body))
return await HandleHelpAsync(payload.From);
@@ -123,6 +132,44 @@ public class WebhooksController : ControllerBase
$"Reply START to re-subscribe.");
}
// ── START ─────────────────────────────────────────────────────────────────
/// <summary>
/// Processes a START keyword: re-enables SMS for the customer, clears SmsOptedOutAt,
/// logs to NotificationLog, and returns a TwiML confirmation message.
/// </summary>
private async Task<IActionResult> HandleStartAsync(string from)
{
var (customer, digits10) = await FindCustomerByPhoneAsync(from);
if (customer == null)
{
_logger.LogWarning("Twilio START from {From} — no matching customer found", from);
return TwimlMessage("You have been re-subscribed and will receive messages again.");
}
var companyName = await GetCompanyNameAsync(customer.CompanyId);
if (!customer.NotifyBySms)
{
customer.NotifyBySms = true;
customer.SmsOptedOutAt = null;
customer.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("Customer {CustomerId} re-subscribed to SMS via START reply", customer.Id);
}
await WriteInboundLogAsync(
NotificationType.SmsInboundStart,
customer,
from,
"START");
return TwimlMessage(
$"{companyName}: You have been re-subscribed and will receive messages again. " +
$"Reply STOP to unsubscribe at any time.");
}
// ── HELP ──────────────────────────────────────────────────────────────────
/// <summary>
@@ -1097,8 +1097,8 @@ public static class HelpKnowledgeBase
**Send SMS button:**
On any completed job's Details page, Company Admins and Managers see a **Send SMS** button that opens the same compose modal, allowing you to send a follow-up message at any time.
**Customer opt-out:**
If a customer replies STOP to any message, they are automatically opted out and will not receive further SMS messages. They can reply START to re-subscribe.
**Customer opt-out and opt back in:**
If a customer replies STOP (or STOPALL, CANCEL, END, QUIT, UNSUBSCRIBE) to any message, they are automatically opted out and will not receive further SMS messages. If they change their mind, they can reply START, YES, or UNSTOP to re-subscribe automatically no action needed from your staff. You can also manually toggle SMS back on from their customer record.
### Platform Announcements
Occasional platform-wide announcements from the Powder Coating Logix team are delivered directly to your notification bell not as page banners. These may cover new features, scheduled maintenance, or policy updates. They appear as **Announcement** type items in the bell dropdown.
@@ -416,7 +416,9 @@
<div>
Every outbound SMS automatically includes opt-out instructions ("Reply STOP to opt out"). If a
customer replies STOP, they are immediately opted out and will receive no further messages.
You can re-enable them on their customer record if they later ask to be re-subscribed.
If they change their mind, they can reply <strong>START</strong>, <strong>YES</strong>, or <strong>UNSTOP</strong>
to re-subscribe automatically — no action needed from your staff. You can also manually re-enable
SMS on their customer record.
</div>
</div>
</section>