```html

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

During this development session, we deployed a complete vendor referral program infrastructure for JADA's Ship Captain Crew platform. The system tracks clicks on vendor-specific referral links, logs them to DynamoDB, and redirects guests to the main site with UTM parameters for analytics. Here's how we built it.

What We Built

The referral system consists of three core components:

  • A GET /ref/{code} endpoint in the SCC Lambda function that handles incoming referral clicks
  • DynamoDB table entries that track vendor name, timestamp, and cumulative click counts
  • CloudFront and API Gateway routing to expose the endpoint publicly without authentication
  • Vendor outreach HTML templates and an approval dashboard for tracking vendor communications

Seven vendors are currently integrated with live referral links:

  • 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

Lambda Handler Implementation

The referral endpoint lives in /tmp/scc-deployed/lambda_function.py, the deployed version of the SCC Lambda. The handle_referral_click function processes incoming requests and performs three critical operations:

  1. Parse the vendor code: Extract the code from the URL path (e.g., solare from /ref/solare)
  2. Log to DynamoDB: Write a record with the vendor name, ISO timestamp, and increment the running click count in the referral tracking table
  3. Redirect with UTM parameters: Send a 302 redirect to queenofsandiego.com with GA4 tracking parameters

The function signature looks like this:

def handle_referral_click(vendor_code):
    # Validate vendor exists in config
    # Write to DynamoDB with timestamp
    # Return 302 redirect with UTM params
    return {
        'statusCode': 302,
        'headers': {
            'Location': f'https://queenofsandiego.com?utm_source=referral&utm_medium={vendor_code}'
        }
    }

The key design decision here was to keep the endpoint unauthenticated and public. Referral links must work for anonymous guests, so we removed the standard API Gateway authorizer from this specific route.

CloudFront and API Gateway Routing

Getting the /ref endpoint live required careful routing configuration. The SCC domain (shipcaptaincrew.queenofsandiego.com) uses CloudFront as a CDN in front of an API Gateway endpoint. Initially, the /ref path was caught by the API Gateway's default /{proxy+} route, which required authentication.

We solved this by:

  1. Adding a new CloudFront behavior: Created a behavior for /ref/* that routes directly to the API Gateway origin without caching (cache TTL = 0)
  2. Updating API Gateway: Modified the SCC API Gateway to treat /ref as a public route exempt from the Lambda authorizer
  3. Invalidating CloudFront cache: Ran a cache invalidation on /ref/* to clear any stale responses

The CloudFront configuration change was made via AWS CLI:

# Get the current distribution config
aws cloudfront get-distribution-config --id  > dist-config.json

# Add new behavior for /ref/* (inserted before the default behavior)
# with ForwardedValues set to forward query strings and all headers

# Update the distribution
aws cloudfront update-distribution --distribution-config file://dist-config.json --id 

# Invalidate the cache
aws cloudfront create-invalidation --distribution-id  --paths "/ref/*"

We avoided caching on referral redirects because click counts are real-time metrics and outdated 302 responses would send users to the wrong destination.

DynamoDB Schema

The referral click logs are stored in a DynamoDB table with the following structure:

  • Partition Key: vendor_name (e.g., "Solare", "Board & Brew")
  • Sort Key: timestamp (ISO 8601 format)
  • Attributes:
    • click_count (Number) — cumulative clicks for that vendor
    • referral_code (String) — the URL-safe vendor code

Each request increments click_count using DynamoDB's atomic ADD operation, ensuring accurate counts even under concurrent traffic.

Vendor Outreach and Approval Workflow

To track vendor communications, we created an approval dashboard. This involved:

  1. HTML email templates: Drafted 3 vendor outreach emails as standalone HTML files and uploaded them to S3 at paths like s3://jada-assets/vendor-referral/templates/boardbrew-preview.html
  2. Preview page: Created /tmp/vendor-referral-outreach.html with embedded iframes for each vendor email, allowing stakeholders to review before final send
  3. Dashboard task card: Used the internal dashboard API to create an approval card that links to the preview page and tracks send status

The dashboard integration command looked like:

python update_dashboard.py \
  --action add-task \
  --card-type vendor-approval \
  --vendor boardbrew \
  --preview-url https://shipcaptaincrew.queenofsandiego.com/vendor-referral-outreach.html#boardbrew \
  --status pending-send

Key Infrastructure Decisions

Why no authentication on /ref? Referral links are shared externally with guests who have no JADA account. Requiring authentication would break the entire user journey. Instead, we rate-limit at the infrastructure level using API Gateway throttling.

Why DynamoDB over S3? Click counts must be updated in real