```html

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

Over the past development session, we built and deployed a complete referral tracking infrastructure for the JADA vendor partner program. This post details the technical decisions, architecture patterns, and deployment steps that enable real-time click tracking and partner attribution across our ecosystem.

What Was Done

We implemented a referral tracking system that:

  • Created a GET /ref/{code} endpoint on the ShipCaptainCrew Lambda function
  • Integrated DynamoDB for click logging with vendor attribution
  • Set up CloudFront cache behaviors to route /ref/* requests to the Lambda origin
  • Built vendor outreach HTML templates with embedded preview pages
  • Created dashboard approval cards for partner communications

Technical Architecture

Referral Link Structure

The system uses vanity URL codes mapped to vendor names:

https://shipcaptaincrew.queenofsandiego.com/ref/{vendor_code}

Examples:
  /ref/solare → Solare restaurant
  /ref/boardbrew → Board & Brew
  /ref/wholefoods → Whole Foods
  /ref/totalwine → Total Wine
  /ref/smallgoods → Smallgoods
  /ref/puesto → Puesto
  /ref/gbvintners → Gianni Buonomo Vintners

Lambda Handler Implementation

The referral endpoint lives in /tmp/scc-deployed/lambda_function.py and implements the handle_referral_click function. This function:

  1. Accepts a /ref/{code} request from API Gateway
  2. Logs the click to DynamoDB with timestamp and vendor attribution
  3. Redirects the guest to queenofsandiego.com with UTM parameters for GA4 tracking
  4. Returns a 302 redirect response

The endpoint was deployed as a public route (no authorization required) because partner vendors need to share these links externally. This required modifying the Lambda authorization logic to bypass the auth gate for paths matching /ref/*.

DynamoDB Click Logging

Each referral click is recorded with:

  • Vendor name (from the URL code)
  • Timestamp (ISO 8601 format)
  • Running click count (incremented atomically)
  • Source tracking (for multi-channel analysis)

This allows real-time dashboard visibility into which vendor partnerships are driving traffic.

Infrastructure Changes

CloudFront Distribution Update

The CloudFront distribution for shipcaptaincrew.queenofsandiego.com required a new cache behavior to route referral requests to the Lambda origin:

  • Path Pattern: /ref/*
  • Origin: API Gateway origin (not S3)
  • Allowed HTTP Methods: GET, HEAD
  • Cache TTL: 0 seconds (no caching; each click must hit Lambda)
  • Compress: Disabled (redirects don't benefit from compression)

This was deployed via CloudFront API update and required invalidating the /ref/* cache path after each Lambda code change to ensure fresh requests reached the latest function version.

API Gateway Configuration

The SCC API Gateway was already exposing the Lambda function but needed authorization logic inspection to confirm the /ref/* path was properly configured as public. The key insight: authorizer checks must happen after routing, so the Lambda code itself gates access based on path patterns.

Lambda Code Structure

Before the authorization gate in the Lambda handler, we added path-based routing logic:

# Pseudo-code structure (no actual secrets shown)
def lambda_handler(event, context):
    path = event.get('path', '')
    
    # Public endpoints bypass auth
    if path.startswith('/ref/'):
        return handle_referral_click(event)
    
    # All other paths require authorization
    auth_result = check_authorization(event)
    if not auth_result['authorized']:
        return 401_unauthorized_response()
    
    # Route to appropriate handler
    return route_to_handler(event)

Key Technical Decisions

Why No Caching for Referral Clicks

Setting CloudFront TTL to 0 for /ref/* was intentional. Caching would cause multiple clicks from the same IP to hit CloudFront's edge cache instead of the Lambda origin, resulting in undercounted clicks. Since these are redirect responses (small payloads), the performance cost is negligible compared to data accuracy.

Why 302 Redirects Instead of Server-Side Tracking Pixels

Using HTTP redirects with UTM parameters provides:

  • GA4 attribution at the landing page (organic attribution model)
  • Browser history tracking (users see the journey in their back button)
  • No JavaScript dependency on the partner's shared link
  • Clear separation: click tracking happens in our system, conversion tracking happens in GA4

Why Public Endpoint Without API Key

The /ref endpoint intentionally lacks authentication because:

  • Vendors share these URLs on social media, websites, and in conversations
  • Adding API keys would require embedding credentials in the links (security anti-pattern)
  • Anyone can click any vendor link, so there's no secret to protect
  • DynamoDB rate limiting and IP-based analysis can detect abuse if needed

Deployment Steps

The deployment involved sequential infrastructure changes to avoid outages:

  1. Lambda Code Update: Added handle_referral_click function and updated routing logic in lambda_function.py
  2. Lambda Deployment: Published new version and updated alias to point to the new code
  3. CloudFront Behavior Addition: Added /ref/* cache behavior via API call
  4. Cache Invalidation: Invalidated /ref/* to purge any stale responses
  5. Testing: Verified redirect behavior and DynamoDB logging for sample vendor codes

Vendor Outreach Integration

The referral tracking system was paired with vendor communication templates:

  • HTML email previews were built for each vendor partner in /tmp/vendor-referral-outreach.html
  • Dashboard approval cards created task entries linking to preview pages hosted on S3
  • Each template embeds the vendor's unique referral code and provides social/email sharing options

What's Next

The system is live and tracking referral clicks across all