Building a Self-Contained Tenant Portal with Email-to-Payment Logging: Separating queenofsandiego.com from dangerouscentaur.com

What Was Done

We built a complete tenant management system for a rental property at 3028 51st Street, San Diego (92105) that operates entirely within the dangerouscentaur.com domain, completely divorced from the primary business domain queenofsandiego.com. This included:

  • Creating a tenant hub portal at 3028fiftyfirststreet.92105.dangerouscentaur.com
  • Generating and distributing tenant credentials via a dedicated SES email identity
  • Building an email-to-payment-log system that captures Zelle transfers forwarded by the landlord
  • Automating payment logging through Google Apps Script and AWS Lambda integration
  • Ensuring complete domain isolation to prevent security and operational cross-contamination

The Critical Architecture Decision: Domain Separation

The original system attempted to send tenant credentials from queenofsandiego.com. This created several problems:

  • Security boundaries: Tenant data and business operations mixed in the same domain
  • Email reputation: Tenant interactions would impact the primary business domain's sender reputation
  • Operational clarity: Tenant systems should be completely independent from agent operations
  • Compliance: Easier to audit and compartmentalize tenant information separately

The solution: establish dangerouscentaur.com as a purpose-built infrastructure domain for property management, with no overlap to queenofsandiego.com systems.

Tenant Portal Infrastructure

Frontend: Static HTML Portal

Located at: /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html

The portal includes:

  • Tenant login with temporary password initialization
  • Dashboard displaying lease information and payment status
  • A "Receipts" section that dynamically loads from the backend
  • Client-side rendering of payment history from a JSON receipt store

The portal is deployed to an S3 bucket and served through CloudFront with automatic cache invalidation. After credential generation, we invalidated the full distribution to ensure fresh content delivery.

Backend: Lambda Functions

Receipt Action Lambda: lambda-receipt-action

This Lambda function handles all payment-related operations. Located in the project structure at scripts/lambda-receipt-action/lambda_function.py, it:

  • Exposes a Lambda Function URL for direct HTTP invocation
  • Validates requests using an ADMIN_TOKEN environment variable
  • Reads from and writes to a receipts.json file stored in S3
  • Supports the log_rent_payment action for recording Zelle transfers
  • Returns properly formatted JSON responses for frontend consumption

The Lambda function reads environment variables at runtime, including the admin token for request validation. This prevents token hardcoding and allows credential rotation without redeployment.

Email Parser Lambda: lambda-email-parser

A newly created Lambda function at scripts/lambda-email-parser/lambda_function.py that:

  • Receives forwarded Zelle payment emails from Google Apps Script
  • Parses email content to extract payment amounts and recipient information
  • Invokes the receipt-action Lambda to log the payment
  • Returns confirmation to the Apps Script for user feedback

Email Infrastructure: SES Domain Setup

Instead of using queenofsandiego.com for tenant communications, we established dangerouscentaur.com as the sender domain in AWS SES.

Setup steps:

aws ses verify-domain-identity \
  --domain dangerouscentaur.com \
  --region us-west-2

AWS SES returns DKIM tokens that must be added to the domain's DNS records via Namecheap. These tokens enable DKIM signing of outbound emails, improving deliverability and preventing spoofing.

For actual email sending, we configured an ImprovMX alias to handle sends and receives:

  • Alias: tenants@dangerouscentaur.com
  • Verified in SES: As a sending identity
  • ImprovMX routing: Forwards to the landlord's personal email for receipt

This pattern allows tenants to reply to a professional domain address while ensuring the landlord receives all communications in their primary inbox.

Credential Distribution Flow

Tenants were sent credentials via SES with the following process:

aws ses send-email \
  --from tenants@dangerouscentaur.com \
  --to tenant@example.com \
  --subject "Your Tenant Hub Credentials" \
  --text "Your temporary password is: [GENERATED_PASS]..." \
  --region us-west-2

Fresh temporary passwords were generated using secure random generation, hashed (never stored in plain text), and embedded only in the initial credential email. The tenant portal requires password change on first login.

Zelle Payment Logging System

The core innovation: automating payment logging from email forwards without manual data entry.

Architecture Flow

  1. Landlord receives Zelle payment notification from their bank
  2. Landlord forwards the email to tenants@dangerouscentaur.com
  3. ImprovMX webhook triggers a Google Apps Script deployment
  4. Apps Script parses the email content and extracts payment details
  5. Apps Script calls the email-parser Lambda with payment metadata
  6. Email-parser Lambda invokes the receipt-action Lambda to log the payment
  7. Payment appears in tenant portal receipts within seconds

Google Apps Script Implementation

File: /Users/cb/Documents/repos/sites/queenofsandiego.com/WarmLeadResponder.gs

We extended the existing Apps Script project to include a new command handler:

  • doPost(e) - Receives webhook payloads from email system
  • handleZelleForward() - Parses Zelle email content for amount, date, and sender
  • logRentPayment() - Calls the receipt-action Lambda with extracted data

The script uses the Admin Token (stored securely in Apps Script environment) to authenticate with the Lambda function URL.

Lambda Receipt-Action Update

Added a new action handler to lambda_function.py