Politician Email Automation - Implementation Plan

Overview

Automatically send pre-written emails to politicians on behalf of pledge signers, using a transactional email service with Reply-To set to the constituent's email address.


Platform Recommendation

Quick Comparison

Platform Free Tier Paid API Quality Deliverability Best For
Postmark None $15/mo for 10k Excellent Best-in-class Transactional to strict filters
Mailgun 5k/mo (3 months) ~$0.80/1k emails Excellent Very good Developer-friendly, cheap
SendGrid 100/day $20/mo for 50k Good Mixed (shared IPs) High volume marketing
Amazon SES 62k/mo (if in EC2) $0.10/1k Good Good AWS shops

Recommendation: Postmark or Mailgun

Why not SendGrid for this use case: - Free/low tiers share IP pools with spammers → deliverability issues - Government email systems have aggressive spam filters - You're sending to .gov and .ca.gov addresses — deliverability matters more than volume

Postmark is purpose-built for transactional email and has the best reputation for reaching strict inboxes (banks, government, enterprise). $15/mo covers 10k emails which is way more than needed.

Mailgun is the budget option with excellent API. Pay-as-you-go after trial is very cheap.

For emails to politicians where you want maximum "did it actually arrive" confidence: Postmark.


Implementation Plan

Phase 1: Email Service Setup (API-driven)

1.1 Create Postmark Account - Sign up at postmarkapp.com - Create a "Server" (their term for a project/environment) - Get API token (Server API Token)

1.2 Domain Verification (DNS) Add these records to californiaforever.com DNS: - SPF record (TXT) - DKIM record (TXT) - Return-Path CNAME

Postmark provides exact values in dashboard. Can be done via registrar or Flywheel if DNS is there.

1.3 Sender Signature Verify pledges@californiaforever.com (or similar) as authorized sender.


Phase 2: WordPress Integration

2.1 Create Integration File

New file: inc/integrations/politician-email-sender.php

/**
 * Politician Email Sender
 * Sends automated emails to politicians via Postmark API
 */

// API credentials (store in wp-config.php)
// define('POSTMARK_API_TOKEN', 'xxxxx');

/**
 * Send politician email on behalf of pledge signer
 */
function cf_send_politician_email( $args ) {
    $defaults = array(
        'constituent_name'  => '',
        'constituent_email' => '',
        'constituent_zip'   => '',
        'politician_name'   => '',
        'politician_email'  => '',
        'politician_title'  => '',
    );
    $args = wp_parse_args( $args, $defaults );

    // Build email
    $subject = 'Support California Forever - Break Ground in 2026';

    $body = sprintf(
        "Dear %s,\n\n" .
        "I am writing to express my support for California Forever and the East Solano Plan...\n\n" .
        "... [rest of template] ...\n\n" .
        "Sincerely,\n" .
        "%s\n" .
        "%s\n" .
        "%s",
        $args['politician_name'],
        $args['constituent_name'],
        $args['constituent_email'],
        $args['constituent_zip']
    );

    // Postmark API call
    $response = wp_remote_post(
        'https://api.postmarkapp.com/email',
        array(
            'headers' => array(
                'Accept'                  => 'application/json',
                'Content-Type'            => 'application/json',
                'X-Postmark-Server-Token' => POSTMARK_API_TOKEN,
            ),
            'body' => wp_json_encode( array(
                'From'          => 'California Forever Pledges <pledges@californiaforever.com>',
                'To'            => $args['politician_email'],
                'ReplyTo'       => $args['constituent_email'],
                'Subject'       => $subject,
                'TextBody'      => $body,
                'Tag'           => 'politician-outreach',
                'TrackOpens'    => false,  // Don't track - feels surveillance-y
                'TrackLinks'    => 'None',
                'MessageStream' => 'outbound',
            ) ),
        )
    );

    // Log result
    $result = json_decode( wp_remote_retrieve_body( $response ), true );

    return array(
        'success'    => ! is_wp_error( $response ) && isset( $result['MessageID'] ),
        'message_id' => $result['MessageID'] ?? null,
        'error'      => $result['Message'] ?? null,
    );
}

2.2 Hook into Gravity Forms Submission

Modify existing californiaforever_create_pledge_from_gf() in pledge-signers.php:

// After creating pledge signer post...

// Look up politician by zip
$politician = cf_get_politician_by_zip( $zip_code );

if ( $politician && ! empty( $email ) ) {
    $email_result = cf_send_politician_email( array(
        'constituent_name'  => trim( $first_name . ' ' . $last_name ),
        'constituent_email' => $email,
        'constituent_zip'   => $zip_code,
        'politician_name'   => $politician['name'],
        'politician_email'  => $politician['email'],
        'politician_title'  => $politician['title'],
    ) );

    // Store result on pledge signer post
    update_post_meta( $post_id, '_politician_email_sent', $email_result['success'] ? 'yes' : 'no' );
    update_post_meta( $post_id, '_politician_email_id', $email_result['message_id'] );
}

2.3 Extract Politician Lookup

Move $politician_map from module template to shared function:

/**
 * Get politician data by zip code
 */
function cf_get_politician_by_zip( $zip_code ) {
    $politician_map = array(
        '94510' => array(
            'name'  => 'Senator Maria Gonzalez',
            'email' => 'maria.gonzalez@senate.ca.gov',
            'title' => 'State Senator, District 3',
        ),
        // ... rest of map
    );

    $zip_code = preg_replace( '/[^0-9]/', '', $zip_code );

    if ( strlen( $zip_code ) === 5 && isset( $politician_map[ $zip_code ] ) ) {
        return $politician_map[ $zip_code ];
    }

    return null;
}

Phase 3: Thank You Page Update

Current behavior: Shows copy/paste email template

New behavior: - If email was sent → "Your message has been sent to [Politician Name]!" - If no zip match → Current fallback (find your representative link)

Pass success state via URL parameter: ?zip=94533&sent=1


Phase 4: Admin Visibility

4.1 Add column to Pledge Signers list

Name Organization Status Email Sent Date Signed
John Doe Acme Corp Approved ✓ Sent Jan 15
Jane Smith Pending ✗ No match Jan 14

4.2 Store metadata


Templates

No fancy templates needed.

Postmark (and most transactional services) support: - Plain text (TextBody) — recommended for this use case - HTML (HtmlBody) — overkill for a constituent letter

Politicians receive thousands of emails. A simple, plain-text letter feels more authentic than a branded HTML template. Keep it plain text.


Files to Create/Modify

File Action Purpose
inc/integrations/postmark-email.php Create API integration + send function
inc/post-types/pledge-signers.php Modify Hook email send into form submission
inc/politician-data.php Create Shared politician lookup function
inc/modules/page_modules/politician_email_cta.php Modify Update to show "sent" confirmation
wp-config.php Modify Add API token constant

Cost Estimate

Postmark: $15/month flat - Includes 10,000 emails - Way more than needed for 20-100/day

Mailgun alternative: ~$0.80 per 1,000 emails after free trial - 1,000 emails = $0.80 - Basically free at this volume


Security Considerations

  1. API token stored in wp-config.php, not in theme files
  2. Rate limiting — Postmark handles this, but could add WordPress-side throttle if needed
  3. Validation — Only send if email format is valid, zip matches, form is legitimate
  4. No PII logging — Don't log constituent emails in debug logs

Testing Plan

  1. Set up Postmark with test/sandbox mode
  2. Send test emails to own addresses
  3. Verify Reply-To works correctly
  4. Test with invalid zips (should gracefully skip)
  5. Check admin column displays correctly
  6. Test thank-you page messaging

Questions for Client

  1. Sender address: pledges@californiaforever.com? Or different?
  2. Email copy: Use current template or do they want to revise?
  3. Approval timing: Send immediately on form submit, or only after pledge is approved? - Recommend: Send immediately (more responsive constituent experience)