Self-Hosting
Self-hosting is optional. Most users can use the CDN snippet to load the SDK from
cdn.vendodata.com— no deployment needed for the SDK itself. You still need to deploy the ingestion API to receive events, but the SDK can be served from the CDN.
The ingestion API is designed to run on Google Cloud Run, but it works anywhere you can run a Node.js service (container, VM, serverless).
Prerequisites
- Google Cloud project with BigQuery enabled
- Service account with BigQuery Data Editor role
- Docker (for building the container image)
gcloudCLI configured
Step 1: Provision BigQuery
Create the dataset and tables before starting the API. You have three options:
- Onboarding CLI (recommended) — Run the one-time setup CLI from the
web-tracking-onboardingrepo - Manual — Create tables from the JSON schemas in
packages/schemas/bigquery - Admin API (demo only) — Enable admin endpoints and call
POST /admin/setupwith a service account key
Step 2: Build the Container
cd packages/ingest-api
docker build -t vendo-event-streaming -f Dockerfile ../..The Dockerfile builds both the SDK and the API, so the container can serve the SDK at /v1/sdk.js.
Step 3: Push to Container Registry
export PROJECT_ID=your-gcp-project
export REGION=us-central1
export IMAGE=gcr.io/$PROJECT_ID/vendo-event-streaming:latest
docker tag vendo-event-streaming $IMAGE
docker push $IMAGEStep 4: Deploy to Cloud Run
gcloud run deploy vendo-event-streaming \
--image=$IMAGE \
--region=$REGION \
--platform=managed \
--allow-unauthenticated \
--concurrency=3 \
--memory=4Gi \
--max-instances=3 \
--timeout=3600s \
--port=8080 \
--set-env-vars="WRITE_KEY=your-secret-write-key" \
--set-env-vars="BQ_PROJECT=$PROJECT_ID" \
--set-env-vars="BQ_DATASET=web_tracking"Step 5: Set Up a First-Party Domain
To avoid ad blockers, serve the API from a subdomain of your site:
- Create a CNAME record:
track.yourdomain.com→ your Cloud Run URL - Configure the custom domain in Cloud Run or use a load balancer
- Update the SDK snippet to use your domain:
vendo('init', 'YOUR_WRITE_KEY', {
host: 'https://track.yourdomain.com',
trackPageViews: true
});When using a first-party domain, event data is sent to your domain as a first-party request. If you also serve the SDK from your domain (via /v1/sdk.js), both the script and the data stay on your domain.
Environment Variables
Required (Static Auth Mode)
| Variable | Description |
|---|---|
WRITE_KEY | Secret write key that the SDK must include in every request |
BQ_PROJECT | Google Cloud project ID |
BQ_DATASET | BigQuery dataset name (e.g., web_tracking) |
Required (Firebase Auth Mode)
| Variable | Description |
|---|---|
FIREBASE_AUTH_ENABLED | Set to true to enable per-merchant authentication |
FIREBASE_SERVICE_ACCOUNT_KEY | Base64-encoded Firebase service account JSON (omit on Cloud Run for ADC) |
Optional
| Variable | Default | Description |
|---|---|---|
PORT | 8080 | HTTP port |
BQ_TABLE_EVENTS | events | Events table name |
BQ_TABLE_USERS | users | Users table name |
BQ_TABLE_GROUPS | groups | Groups table name |
BQ_TABLE_ALIASES | aliases | Aliases table name |
BQ_DISABLED | false | Set to true to skip BigQuery inserts (local dev) |
SDK_JS_PATH | Auto-detected | Override the path to the compiled SDK file |
Demo/Admin (Disable in Production)
| Variable | Description |
|---|---|
DEMO_MODE | Set to true to enable demo features |
ADMIN_SETUP_ENABLED | Set to true to enable admin endpoints |
ADMIN_SETUP_TOKEN | Shared secret for admin endpoint auth |
Local Development
Run the API locally without BigQuery:
npm install
npm run build:sdk
npm run build:api
WRITE_KEY=dev \
BQ_PROJECT=test \
BQ_DATASET=test \
BQ_DISABLED=true \
node packages/ingest-api/dist/index.jsThe API starts on http://localhost:8080. The SDK is served at http://localhost:8080/v1/sdk.js.
Demo UI
A built-in demo UI is available for testing:
npm run devThis starts a demo server on http://localhost:3001 with a visual interface to send events and inspect responses.
Architecture
Client Browser
│
▼
Your Domain (track.yourdomain.com)
│
├── GET /v1/sdk.js → Serves compiled SDK
├── POST /collect → Receives events
│ │
│ ├── Validate (JSON schema)
│ ├── Normalize (extract fields, enrich)
│ └── Insert into BigQuery
│ │
│ ├── events table (track + page)
│ ├── users table (identify)
│ ├── groups table (group)
│ └── aliases table (alias)
│
└── GET /health → Health checkMulti-Tenant Mode
When FIREBASE_AUTH_ENABLED=true, the API supports multiple merchants:
- Each merchant gets a unique write key stored in Firestore (
tracking_write_keyscollection) - The API looks up the write key on each request to find the merchant’s config
- Events are routed to the merchant’s configured destinations (Mixpanel, Segment, Customer.io, OneSignal, BigQuery)
See the API Reference for details.
Monitoring
Health Check
Configure your load balancer to poll GET /health. Returns { "ok": true }.
Event Stats
GET /admin/events/stats returns insert counts since server start:
{
"ok": true,
"stats": {
"events": { "total": 1542, "lastInsertAt": "2026-02-08T12:00:00Z" },
"users": { "total": 23, "lastInsertAt": "2026-02-08T11:55:00Z" },
"groups": { "total": 0, "lastInsertAt": null },
"aliases": { "total": 2, "lastInsertAt": "2026-02-08T10:30:00Z" }
}
}Logging
The API logs to stdout. In production, Cloud Run captures these automatically. Key log messages:
Ingest API listening on :8080— StartupSDK loaded from /path/to/sdk.js— SDK file foundcollect error— Request processing failure (includes error details)
Verify Deployment
- Health check:
curl https://track.yourdomain.com/health— should return{"ok":true} - SDK serving: Open
https://track.yourdomain.com/v1/sdk.jsin a browser — should return JavaScript - Event collection: Send a test event:
curl -X POST https://track.yourdomain.com/collect \
-H "Content-Type: application/json" \
-d '{
"writeKey": "your-write-key",
"type": "track",
"messageId": "test-123",
"timestamp": "2026-02-08T12:00:00Z",
"anonymousId": "test-anon",
"event": "Test Event",
"properties": { "source": "curl" }
}'- BigQuery: Confirm rows appear in the
eventstable
Related
- Quickstart — Get started with the SDK
- API Reference — Endpoint and schema reference