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/solarehttps://shipcaptaincrew.queenofsandiego.com/ref/smallgoodshttps://shipcaptaincrew.queenofsandiego.com/ref/puestohttps://shipcaptaincrew.queenofsandiego.com/ref/boardbrewhttps://shipcaptaincrew.queenofsandiego.com/ref/wholefoodshttps://shipcaptaincrew.queenofsandiego.com/ref/totalwinehttps://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:
- Parse the vendor code: Extract the code from the URL path (e.g.,
solarefrom/ref/solare) - Log to DynamoDB: Write a record with the vendor name, ISO timestamp, and increment the running click count in the referral tracking table
- Redirect with UTM parameters: Send a 302 redirect to
queenofsandiego.comwith 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:
- Adding a new CloudFront behavior: Created a behavior for
/ref/*that routes directly to the API Gateway origin without caching (cache TTL = 0) - Updating API Gateway: Modified the SCC API Gateway to treat
/refas a public route exempt from the Lambda authorizer - 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 vendorreferral_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:
- 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 - Preview page: Created
/tmp/vendor-referral-outreach.htmlwith embedded iframes for each vendor email, allowing stakeholders to review before final send - 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