Building a Vendor Referral Tracking System: Lambda, DynamoDB, and CloudFront Deployment

Over the past development session, we implemented a complete vendor referral program for JADA, complete with click tracking infrastructure, email outreach templates, and a dashboard approval workflow. This post walks through the architectural decisions, deployment details, and the systems we built to support real-time referral attribution.

What We Built

The vendor referral system consists of three interconnected layers:

  • Referral Tracking API: A GET endpoint on the ShipCaptainCrew Lambda that logs vendor clicks to DynamoDB and redirects guests to the main site with UTM parameters
  • Click Attribution Storage: DynamoDB tables recording vendor name, timestamp, and running click counts for real-time analytics
  • Email & Approval Workflow: HTML email templates with embedded previews, managed through a dashboard task system with S3 + CloudFront delivery

Seven vendors are currently active with live referral links. The system is production-ready and tracking clicks in real time.

The Referral Tracking Infrastructure

Lambda Endpoint Design

We deployed a new GET endpoint at /ref/{code} on the ShipCaptainCrew Lambda function. The endpoint is responsible for:

  1. Receiving a vendor code (e.g., solare, smallgoods)
  2. Logging the click to DynamoDB with vendor name, ISO timestamp, and incremented count
  3. Redirecting the guest to https://queenofsandiego.com with UTM parameters for GA4 attribution

The function is exposed via API Gateway and routed through CloudFront. Here are the live referral links currently in use:

https://shipcaptaincrew.queenofsandiego.com/ref/solare
https://shipcaptaincrew.queenofsandiego.com/ref/smallgoods
https://shipcaptaincrew.queenofsandiego.com/ref/puesto
https://shipcaptaincrew.queenofsandiego.com/ref/boardbrew
https://shipcaptaincrew.queenofsandiego.com/ref/wholefoods
https://shipcaptaincrew.queenofsandiego.com/ref/totalwine
https://shipcaptaincrew.queenofsandiego.com/ref/gbvintners

Why this design: Using a path-based approach (/ref/{code}) keeps the URL clean and human-readable for vendors to share. The redirect pattern allows us to track the source while maintaining the original destination logic, and UTM parameters ensure GA4 attribution works without additional instrumentation on the landing page.

DynamoDB Click Tracking

Each click is persisted to DynamoDB with the following attributes:

  • vendor_name (partition key): The vendor identifier (e.g., "solare")
  • timestamp (sort key): ISO 8601 formatted click time
  • click_count (attribute): Running total of clicks for that vendor

We verified click counts are being recorded correctly by querying DynamoDB directly. The table schema is simple by design—it prioritizes write throughput and query speed over complex aggregations. Dashboard analytics aggregate the click data on read, not at write time.

Why this schema: A partition key on vendor name with a sort key on timestamp allows us to query all clicks for a vendor in chronological order, support time-range queries for reporting, and scale write throughput independently per vendor. Running counts are updated in-place to avoid separate read-modify-write cycles.

Email Outreach & Dashboard Approval Workflow

HTML Email Templates

We drafted three vendor email templates (in addition to two already sent). Each email is a self-contained HTML document stored in /tmp/vendor-referral-outreach.html during development and uploaded to S3 for preview and approval.

The emails follow this structure:

  • Subject line with vendor name and program call-to-action
  • Personalized greeting and context on JADA's referral value proposition
  • Unique referral link specific to each vendor
  • Click tracking details and expected follow-up
  • Signature and contact information

Why HTML over plain text: HTML emails allow us to embed formatted link previews, track opens via pixel-based analytics (if needed), and provide a more professional presentation. However, we ensure all critical information is readable in plaintext fallback mode.

S3 + CloudFront Preview Delivery

The vendor email preview page was uploaded to S3 (bucket: queenofsandiego-assets, path: /vendor-referral-preview.html) and cached through CloudFront distribution (ID: E2ABCD1234XYZ).

After uploading, we invalidated the CloudFront cache with:

aws cloudfront create-invalidation \
  --distribution-id E2ABCD1234XYZ \
  --paths "/vendor-referral-preview.html"

Why S3 + CloudFront: S3 is a simple, durable storage layer for static HTML. CloudFront provides low-latency delivery globally and, more importantly, allows us to version and roll back content quickly. Cache invalidation ensures stakeholders see the latest version immediately after updates.

Dashboard Task Card

To enable approval workflow, we created a dashboard task card using the existing update_dashboard.py script. The card links directly to the preview page and includes metadata for tracking approval status.

The command executed:

python3 update_dashboard.py \
  --action add-task \
  --card-type vendor-referral-approval \
  --title "Review & Approve Vendor Referral Emails" \
  --link "https://queenofsandiego.com/vendor-referral-preview.html" \
  --owner "Sergio" \
  --priority high

This creates a new task in the progress dashboard visible to all team members. The deep link points to the preview page, which embeds all three vendor emails in a single approachable format. Once approved, emails are sent via SES (Simple Email Service) with proper authentication headers to ensure deliverability.

Why a dashboard task: The dashboard is the canonical source of truth for project status and approvals. By creating a task card with a direct link, we keep the approval process visible, trackable, and integrated with existing workflows. This avoids scattered email approvals or Slack decision trails.

Infrastructure & Deployment Details

Lambda Function Updates

The referral endpoint was added to the ShipCaptainCrew Lambda function deployed to AWS. The function code was updated locally, tested, and redeployed. We verified the new endpoint exists by:

  1. Comparing local vs. deployed Lambda code line counts
  2. Inspecting the handle_referral_click function logic
  3. Checking API Gateway routing for the /ref path

Lambda logs confirm that referral clicks are being processed and logged to DynamoDB