A single batch SMS to an unvetted list isn’t a marketing decision — it’s a liability event. $500–$1,500 per message under TCPA. The compliance rail eliminates the exposure entirely.
Sending a text message to someone who hasn’t consented to receive one is a TCPA violation. Each violation is $500–$1,500 per message. A single batch SMS to an unvetted list isn’t a marketing decision — it’s a liability event. The compliance rail is the architecture that ensures every outbound text has documented consent, every STOP is honored immediately, and the carrier infrastructure is registered properly.
The Anchor build wanted to use SMS for lead follow-up: appointment reminders, price drop alerts, and inquiry acknowledgments. Before sending a single text, the compliance infrastructure had to be in place: TCPA opt-in at the inquiry form, STOP keyword handling, and A2P 10DLC registration to prevent carrier filtering.
Express written consent must be obtained before sending marketing SMS. The consent must be specific about what the sender will send, and it must be separate from any other agreement:
function [client]_render_sms_consent_checkbox(): void {
?>
<div class="form-field form-field--consent">
<label class="consent-label">
<input type="checkbox" name="sms_consent" value="1" required>
<span>I agree to receive text messages from [Client Dealer Name] about my inquiry,
including appointment reminders and inventory updates. Message and data rates may apply.
Reply STOP to opt out at any time.</span>
</label>
</div>
<?php
}
The consent — including timestamp, source URL, and IP address — is stored with the lead:
function [client]_store_sms_consent( int $lead_id, bool $consented ): void {
if ( ! $consented ) {
return;
}
update_post_meta( $lead_id, '[client]_sms_consent', 1 );
update_post_meta( $lead_id, '[client]_sms_consent_at', current_time( 'mysql' ) );
update_post_meta( $lead_id, '[client]_sms_consent_source', wp_unslash( $_SERVER['HTTP_REFERER'] ?? '' ) );
update_post_meta( $lead_id, '[client]_sms_consent_ip',
sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '' ) );
}
Every outbound SMS function checks consent before sending. If consent is not on record, the message is not sent — no exceptions:
function [client]_can_send_sms( int $lead_id ): bool {
if ( get_post_meta( $lead_id, '[client]_sms_opted_out', true ) ) {
return false;
}
return (bool) get_post_meta( $lead_id, '[client]_sms_consent', true );
}
function [client]_send_sms( int $lead_id, string $message ): bool {
if ( ! [client]_can_send_sms( $lead_id ) ) {
return false; // Silently skip — this is expected, not an error
}
$phone = get_post_meta( $lead_id, '[client]_phone', true );
if ( ! $phone ) {
return false;
}
return [client]_dispatch_sms( $phone, $message );
}
Inbound SMS messages arrive via webhook. Any message containing a STOP keyword must be honored immediately — in the same request cycle as the inbound message:
add_action( 'rest_api_init', function() {
register_rest_route( '[client]/v1', '/sms-inbound', [
'methods' => 'POST',
'callback' => '[client]_handle_inbound_sms',
'permission_callback' => '__return_true',
] );
} );
function [client]_handle_inbound_sms( WP_REST_Request $request ): WP_REST_Response {
$from = sanitize_text_field( $request->get_param( 'From' ) ?? '' );
$body = strtoupper( trim( sanitize_text_field( $request->get_param( 'Body' ) ?? '' ) ) );
$stop_keywords = [ 'STOP', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT' ];
if ( in_array( $body, $stop_keywords, true ) ) {
[client]_opt_out_sms_by_phone( $from );
[client]_dispatch_sms( $from, 'You have been unsubscribed. No further messages will be sent.' );
} else {
[client]_handle_lead_reply_sms( $from, $body );
}
return new WP_REST_Response( '', 200 );
}
function [client]_opt_out_sms_by_phone( string $phone ): void {
global $wpdb;
$lead_ids = $wpdb->get_col( $wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = '[client]_phone' AND meta_value = %s",
[client]_normalize_phone( $phone )
) );
foreach ( $lead_ids as $lead_id ) {
update_post_meta( $lead_id, '[client]_sms_opted_out', 1 );
update_post_meta( $lead_id, '[client]_sms_opted_out_at', current_time( 'mysql' ) );
}
}
Application-to-Person (A2P) 10DLC requires three registrations before sending at commercial volume:
The vetting score from Brand registration determines throughput:
For any business sending more than a few dozen SMS per day, enhanced vetting is the correct tier.
TCPA compliance is not optional. The statute provides for $500 per non-consensual message and $1,500 for willful violations. A sequence that sends 5 messages to 100 unconsented leads is $250,000–$750,000 in potential exposure. The compliance rail costs a few hours to build and eliminates the liability entirely.
Carrier filtering (without A2P registration) produces delivered-but-unread messages at best and carrier-level blocks at worst. A2P 10DLC registration is what distinguishes commercial SMS from potential spam in the carrier’s routing infrastructure.
A2P registered: Brand approved, two Campaigns registered (Customer Care and Appointment Reminders). Throughput tier: Enhanced. Zero carrier rejections in 90 days. STOP keyword handler tested with real unsubscribes: 4 opt-outs processed correctly, all within the same request cycle as the inbound message.
Every lesson stays free — no account, no paywall, no email gate, ever. But if you’d rather have this system standing on your business than wire all 48 lessons yourself, leave your email. We’ll send you a direct line to a build — and you’ll be first to hear when we add new tools to the curriculum.
None of this gates a single lesson. The curriculum was free before you got here and it stays that way.
You came here to understand the system, and now you do. If you’d rather have it standing on your business than spend the next three months wiring it yourself, GAP Concierge is the same architecture from these lessons — a white-label AI agent that knows your catalog and captures your leads — set up for you, from $97/mo.
See GAP Concierge →