The difference between a general inquiry and a credit application is the difference between a cold lead and a hot one. Your forms should know the difference.
One contact form for every possible visitor intent is like having one checkout lane in a grocery store — people with different needs queue up together, the experience is worse for everyone, and you can't prioritize the person who's ready to buy right now over the person who's just browsing.
The Anchor build before Era 2 had exactly one form: name, email, phone, message. It sat in the footer of most pages. Every submission landed in the same inbox with no context about what the visitor actually wanted.
A credit application — someone who wants financing approval — arrived in the same format as a general "I have a question about hours." Both got the same delayed response. The sales team had no way to know which was urgent without reading every email. The form's job was to capture intent, and it was failing at that entirely.
Each form is placed where the intent is highest and captures the fields that surface actually needs:
All forms submit via AJAX to a single WordPress action. The action is registered for both logged-in and non-logged-in users — all visitors are unauthenticated from WordPress's perspective.
add_action( 'wp_ajax_[client]_lead_submit', '[client]_handle_lead_submit' );
add_action( 'wp_ajax_nopriv_[client]_lead_submit', '[client]_handle_lead_submit' );
function [client]_handle_lead_submit() {
$validation = [client]_validate_submission( $_POST );
if ( is_wp_error( $validation ) ) {
wp_send_json_error( [ 'message' => $validation->get_error_message() ] );
return;
}
$lead_data = [client]_build_lead_data( $_POST );
$lead_id = [client]_create_lead( $lead_data );
if ( ! $lead_id ) {
wp_send_json_error( [ 'message' => 'Submission failed. Please try again.' ] );
return;
}
[client]_route_lead_notification( $lead_id, $lead_data['source_form'] );
wp_send_json_success( [ 'lead_id' => $lead_id ] );
}
Three layers of validation before any lead data is processed or stored.
function [client]_validate_submission( $post_data ): WP_Error|true {
// Layer 1: nonce check
if ( ! wp_verify_nonce( $post_data['_wpnonce'] ?? '', '[client]_lead_submit' ) ) {
return new WP_Error( 'invalid_nonce', 'Security check failed.' );
}
// Layer 2: honeypot — bots fill hidden fields, humans don't see them
if ( ! empty( $post_data['website'] ) ) {
return new WP_Error( 'bot_detected', 'Submission rejected.' );
}
// Layer 3: rate limit — 3 submissions per IP per hour
$ip_hash = md5( $_SERVER['REMOTE_ADDR'] );
$cache_key = '[client]_rl_' . $ip_hash;
$count = (int) get_transient( $cache_key );
if ( $count >= 3 ) {
return new WP_Error( 'rate_limited', 'Too many submissions. Please wait before trying again.' );
}
set_transient( $cache_key, $count + 1, HOUR_IN_SECONDS );
return true;
}
function [client]_route_lead_notification( int $lead_id, string $source_form ) {
$routing = [
'credit_application' => [
'to' => FINANCE_MANAGER_EMAIL,
'subject' => '[HOT] Credit Application',
'urgent' => true,
],
'financing' => [
'to' => FINANCE_MANAGER_EMAIL,
'subject' => 'Financing Inquiry',
'urgent' => false,
],
'unit_inquiry' => [
'to' => SALES_EMAIL,
'subject' => 'Unit Inquiry',
'urgent' => false,
],
'trade_in' => [
'to' => SALES_EMAIL,
'subject' => 'Trade-In Evaluation Request',
'urgent' => false,
],
'consignment' => [
'to' => SALES_MANAGER_EMAIL,
'subject' => 'Consignment Inquiry',
'urgent' => false,
],
'general' => [
'to' => FRONT_DESK_EMAIL,
'subject' => 'General Inquiry',
'urgent' => false,
],
];
$config = $routing[ $source_form ] ?? $routing['general'];
[client]_send_lead_notification( $lead_id, $config );
}
Intent-matched forms do two things simultaneously: they make the experience better for the visitor (fewer irrelevant fields, the form asks exactly what makes sense given where they are on the site), and they make the sales team's job easier (credit applications are flagged as hot; general inquiries are not).
The sales team was previously triaging every email manually to decide what was urgent. Routing by form type does that triage automatically. Credit applications are flagged [HOT] in the subject line. The sales team has a clear visual signal the moment the email arrives.
Six capture surfaces replaced the single footer form. Within the first month, the distribution of submissions by form type revealed something useful: 35% of submissions were unit inquiries from listing pages, 28% were financing pre-qualifications, and 12% were credit applications. The single general form had been capturing all of this with no differentiation. The finance manager was reading through general inquiries to find financing requests manually.
After routing, the finance manager saw only financing and credit application submissions in their inbox. Response time to hot leads (credit applications) dropped from hours to under 30 minutes in the first week — not because anything technically changed, but because the routing eliminated the triage step entirely.
wp_ajax_ and wp_ajax_nopriv_. Without the nopriv variant, unauthenticated visitors can't submit — which is all of them.display:none or off-screen positioning) but left visible in the HTML so bots find it and fill it.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 →