```html

Building a Tenant Payment Logging System: Zelle-to-Lambda Email Pipeline with SES Domain Separation

Overview

We implemented a complete payment logging system for a multi-tenant property management portal, with a critical requirement: strict domain isolation between the property management business (dangerouscentaur.com) and the real estate agent operations (queenofsandiego.com). The system allows tenants to forward Zelle payment confirmations to an automated email handler that logs deposits directly to the tenant hub, eliminating manual accounting overhead.

What Was Done

  • Provisioned a new SES sender identity on dangerouscentaur.com with proper DKIM verification
  • Generated fresh temporary credentials for tenants and deployed updated portal at https://3028fiftyfirststreet.92105.dangerouscentaur.com/
  • Sent credential emails via SES from dangerouscentaur.com domain (not the previous queenofsandiego.com)
  • Extended the Lambda receipt-action function with a new log_payment admin action
  • Wired Google Apps Script (GAS) command handler to detect forwarded Zelle emails and trigger Lambda payment logging
  • Configured ImprovMX email aliasing for the property portal inbox

Technical Architecture

Frontend: Tenant Hub Portal

The tenant hub is a single-page application hosted at:

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

Updated credentials are embedded directly in the HTML as a JSON data structure (credentials table), accessed by dashboard initialization code. The portal is deployed to S3 and served through CloudFront with cache invalidation to ensure tenants immediately receive their login credentials.

Backend: Lambda Functions

Two Lambda functions handle the payment flow:

  • Receipt-Action Lambda: Located at /scripts/lambda-receipt-action/lambda_function.py, now extended with an admin_log_payment action that validates an admin token and writes payment entries to receipts.json in S3
  • Email-Parser Lambda: Located at /scripts/lambda-email-parser/lambda_function.py, parses incoming emails forwarded to the property management inbox

Both Lambdas are deployed with Function URLs, enabling direct HTTP invocation. The receipt-action Lambda requires an ADMIN_TOKEN environment variable for payment logging, preventing unauthorized deposits from being recorded.

Email Pipeline: SES + GAS + ImprovMX

The email workflow consists of three stages:

  1. Tenant sends Zelle confirmation to a dangerouscentaur.com email alias (e.g., payments@dangerouscentaur.com)
  2. ImprovMX forwards the email to a Google Sheets webhook endpoint managed by Google Apps Script
  3. GAS command handler (in WarmLeadResponder.gs) detects the Zelle payment pattern, extracts amount/date, and calls the Lambda receipt-action function with the admin token
  4. Lambda logs the payment to receipts.json in the property portal S3 bucket

Infrastructure and Configuration

SES Domain Setup

We initiated domain verification for dangerouscentaur.com in AWS SES with DKIM signing enabled. This required:

  • Retrieving three DKIM tokens from the SES console
  • Adding CNAME records to the domain's DNS provider (Namecheap) to complete DKIM verification
  • Verifying the sender identity before production email delivery

This ensures all outbound tenant credentials and system emails originate from dangerouscentaur.com`, not the real estate agent's domain, maintaining clear business separation.

S3 and CloudFront

The tenant portal is stored in an S3 bucket and distributed through CloudFront. When credentials are updated:

  1. Updated index.html is uploaded to S3
  2. CloudFront cache is invalidated (full invalidation with /* pattern)
  3. Tenants receive fresh credentials within seconds

Lambda Environment Variables

The receipt-action Lambda requires:

  • ADMIN_TOKEN: A strong, randomly-generated token used to authenticate payment logging requests from GAS
  • S3 bucket and credentials path for reading/writing receipts.json

This token is stored in repos.env in the local development environment and added to the Lambda function configuration via AWS CLI:

aws lambda update-function-configuration \
  --function-name dangerouscentaur-receipt-action \
  --environment Variables={ADMIN_TOKEN=<token>}

ImprovMX Alias Configuration

ImprovMX provides email aliasing without running a mail server. The setup:

  • Create alias: payments@dangerouscentaur.com
  • Forward to: GAS webhook endpoint (or internal email for manual review during testing)
  • Enable verification to prevent spoofing

This allows the property manager to accept tenant Zelle forwards without exposing personal email addresses.

Key Technical Decisions

Why Separate Domains?

Mixing queenofsandiego.com (real estate agent) with dangerouscentaur.com (property management) creates compliance and branding issues. Tenants should interact exclusively with the property management entity. SES sender verification ensures email deliverability and domain reputation are independent.

Why GAS for Email Command Parsing?

Google Apps Script provides free, serverless email webhook handling integrated with Google Sheets. It's ideal for pattern matching (detecting Zelle emails) and calling Lambda functions without adding infrastructure complexity. The existing WarmLeadResponder.gs command handler already processes structured emails; we extended it to handle payment forwarding.

Why Admin Token on Lambda?

Payment logging modifies the financial record. An admin token prevents a compromised tenant account or misconfigured email rule from adding false deposits. The token is passed in the HTTP request body and validated server-side before writing to receipts.json`.

Why S3 + JSON for Receipts?

The tenant hub reads receipts.json on page load to display payment history. Storing receipts as JSON in S3 keeps the system simple and queryable without a database, and CloudFront caching ensures fast reads. Writes are append-only and atomic within Lambda.

Deployment Steps (Examples)

Generate and deploy new credentials: