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:
- Accepts a
/ref/{code}request from API Gateway - Logs the click to DynamoDB with timestamp and vendor attribution
- Redirects the guest to
queenofsandiego.comwith UTM parameters for GA4 tracking - 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:
- Lambda Code Update: Added
handle_referral_clickfunction and updated routing logic inlambda_function.py - Lambda Deployment: Published new version and updated alias to point to the new code
- CloudFront Behavior Addition: Added
/ref/*cache behavior via API call - Cache Invalidation: Invalidated
/ref/*to purge any stale responses - 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