API Connection Guide
Everything you need to connect the TagDrishti dashboard to real live data. Each section covers exactly which service to connect, what credential to get, which env variable to set, and what the dashboard will call.
USE_DEMO_DATA: true so it works instantly with realistic sample data. Set this to false and fill in the CONFIG block once your backend is running to switch to live data.โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ TagDrishti Architecture โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Browser (dashboard.tagdrishti.com) โ โ JWT from Clerk โผ Cloud Run API (gtm-monitor-api.run.app) โ โโโโบ Supabase PostgreSQL โ tenants, workspaces, api_keys, alert_configs โ โโโโบ BigQuery โ tag_events, security_events, consent_snapshots, โ anomaly_alerts, web_vitals_rollups โ โโโโบ Upstash Redis โ last 24h tag status cache (instant dashboard load) โ โโโโบ Resend โ email alerts โ โโโโบ Stripe โ billing portal, usage metering GTM Monitor Script (on client website) โ โ x-api-key header โผ Cloud Run API โ Pub/Sub โ Cloud Function โ BigQuery
The dashboard makes all data calls to your Cloud Run API โ it never directly queries BigQuery or Supabase from the browser. This means one URL to configure and one auth token to handle. All service credentials stay server-side.
In Cloud Run: Set these via gcloud run services update or the Cloud Run Console โ Edit & Deploy โ Variables. In the Dashboard HTML: Update the CONFIG block at the top of the <script> section.
Dashboard HTML โ CONFIG block
| Variable | Where to get it | Required? |
|---|---|---|
| CLOUD_RUN_URL | Cloud Run Console โ Service URL (e.g. https://gtm-monitor-api-xyz.run.app) | Required |
| CLERK_PK | Clerk Dashboard โ API Keys โ Publishable Key | Required |
| USE_DEMO_DATA | Set false once backend is live | Required |
Cloud Run โ backend/.env
| Variable | Where to get it | Required? |
|---|---|---|
| SUPABASE_URL | Supabase Dashboard โ Settings โ API โ Project URL | Required |
| SUPABASE_SERVICE_KEY | Supabase โ Settings โ API โ service_role key (not anon) | Required |
| CLERK_SECRET_KEY | Clerk โ API Keys โ Secret key (starts sk_live_) | Required |
| GOOGLE_CLOUD_PROJECT | GCP Console โ Project ID | Required |
| PUBSUB_TOPIC | Set to gtm-monitor-events (matches schema) | Required |
| BQ_DATASET | Set to gtm_monitor (matches schema) | Required |
| RESEND_API_KEY | Resend Dashboard โ API Keys | Required |
| RESEND_FROM | e.g. alerts@tagdrishti.com (verified domain) | Required |
| STRIPE_SECRET_KEY | Stripe โ Developers โ API Keys โ Secret key | Required |
| STRIPE_WEBHOOK_SECRET | Stripe โ Webhooks โ your endpoint โ Signing secret | Required |
| UPSTASH_REDIS_REST_URL | Upstash Console โ your database โ REST URL | Optional |
| UPSTASH_REDIS_REST_TOKEN | Upstash Console โ your database โ REST Token | Optional |
| TAGDRISHTI_NOTIFY_EMAIL | Your own email โ gets CC on every new signup | Optional |
| FRONTEND_URL | Set to https://dashboard.tagdrishti.com | Optional |
.env to .gitignore. In Cloud Run, set variables via the Console or gcloud run services update --set-env-vars. Use Secret Manager for STRIPE_SECRET_KEY and SUPABASE_SERVICE_KEY in production.These are the routes the dashboard's apiGet() / apiPost() calls expect. All are prefixed with your Cloud Run URL. All require Authorization: Bearer {clerk_jwt} except the health check.
Returns the authenticated user's tenant record and all workspaces. Dashboard uses this to populate the sidebar domain picker, all filter dropdowns, and the settings page.
Response Shape
{
"tenant": { "id", "name", "email", "plan", "plan_event_limit", "stripe_customer_id" },
"workspaces": [{ "id", "name", "domain", "gtm_container", "status", "tags", "api_key" }]
}The most-called endpoint. Powers all 5 stat cards, the main chart, alert feed, tag table, domain health, and security mini-list on the Overview page.
| Query param | Type | Description |
|---|---|---|
| range | string | 1h | 24h | 7d | 30d โ maps to BigQuery date filter |
| workspace_id | uuid | "all" | Filter to single workspace or all workspaces for tenant |
BigQuery queries this endpoint should run
-- stats: use v_tag_health_24h view for fast response SELECT * FROM `gtm_monitor.v_tag_health_24h` WHERE tenant_id = @tenant_id AND workspace_id = @workspace_id -- or omit for all -- chart: hourly bucketing SELECT TIMESTAMP_TRUNC(received_at, HOUR) as hour, COUNTIF(tag_status='success') as fires, COUNTIF(tag_status='failure') as fails, AVG(tag_exec_time_ms) as latency FROM `gtm_monitor.tag_events` WHERE event_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY) AND tenant_id = @tenant_id GROUP BY 1 ORDER BY 1 -- alerts: from anomaly_alerts table, last 6 SELECT * FROM `gtm_monitor.anomaly_alerts` WHERE tenant_id = @tenant_id AND event_date >= CURRENT_DATE() - 1 ORDER BY event_date DESC, received_at DESC LIMIT 6
| Query param | Type | Description |
|---|---|---|
| range | string | 24h | 7d | 30d |
| workspace_id | uuid | "all" | Filter by workspace |
| status | string | all | failure | critical | success | blocked_by_consent |
Source
Query v_tag_health_24h BigQuery view with tenant_id + tag_status filter. Return { tags: [...] }
| Query param | Type | Description |
|---|---|---|
| range | string | Date range for query |
| type | string | all | magecart_detected | csp_violation | sri_missing | unknown_script_domain | network_exfiltration |
Source
Query v_security_summary BigQuery view + security_events table. Return { stats:{total,critical,pci_scope,sri_missing,unknown_domains}, events:[...] }
Query v_consent_compliance BigQuery view. Return { analytics_rate, ads_rate, functional_rate, gpc_count, blocked_count, eu_pct, trend_labels[], trend_analytics[], trend_ads[], trend_gpc[] }
| Query param | Type | Description |
|---|---|---|
| workspace_id | uuid | "all" | Domain filter |
| device | string | all | mobile | desktop | tablet |
Source
Query web_vitals_rollups BigQuery table for yesterday's P75. Return { lcp, cls, inp, fcp, ttfb, lcp_good_pct, lcp_needs_pct, lcp_poor_pct, lcp_trend:[], days:[] }
Query anomaly_alerts BigQuery table filtered by type + severity. Return { alerts: [...] } with full alert records including notification_sent flag.
Read from Supabase workspaces + api_keys tables. Joins to get api_key per workspace for the settings page. Used by Domains page.
Body: { name, domain, gtm_container }. Creates Supabase workspace row + generates API key (td_{uuid}). Returns { workspace, api_key }.
Body: { workspace_id, alert_email, slack_webhook_url, fail_rate_threshold, critical_tag_alert }. Upserts to Supabase alert_configs table.
Body: { name?, email?, retention_days?, children_mode? }. Updates Supabase tenants table for authenticated tenant.
Calls Stripe billingPortal.sessions.create with tenant's stripe_customer_id. Returns { url } โ dashboard opens in new tab.
Runs a lightweight SELECT COUNT(*) on tag_events for today. Returns { status, rows_24h, last_write, usage:{events,domains,sessions} }. Used by BigQuery page.
org_id to identify the tenant.initAuth().@clerk/clerk-sdk-node. Use clerkClient.verifyToken(jwt) in a Fastify pre-handler. Extract sub (user ID) and look up tenant_id in Supabase by clerk_org_id.// dashboard HTML โ initAuth() replacement for production import Clerk from '@clerk/clerk-js'; const clerk = new Clerk(CONFIG.CLERK_PK); await clerk.load(); if (!clerk.user) { // Not signed in โ redirect to sign-in clerk.redirectToSignIn({ returnBackUrl: window.location.href }); return; } // Get fresh JWT for API calls STATE.authToken = await clerk.session.getToken();
// Cloud Run Fastify โ auth middleware import { createClerkClient } from '@clerk/clerk-sdk-node'; const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }); app.addHook('preHandler', async (req, reply) => { try { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) return reply.code(401).send({ error: 'Unauthorized' }); const payload = await clerkClient.verifyToken(token); // Look up tenant from Supabase by Clerk org/user ID const { data: tenant } = await supabase .from('tenants').select('*') .eq('clerk_org_id', payload.org_id || payload.sub).single(); req.tenant = tenant; } catch (e) { reply.code(401).send({ error: 'Invalid token' }); } });
ap-south-1 for India). Copy the Project URL and service_role key from Settings โ API.SUPABASE_URL and SUPABASE_SERVICE_KEY in Cloud Run. Use the service_role key โ not anon. This key bypasses Row Level Security and is needed for server-side operations.service_role key. Never expose this key in frontend code. The anon key is for client-side queries only โ not needed here.Tables used by dashboard
| Table | Used by endpoint | Data |
|---|---|---|
| tenants | /api/me, /api/tenants/me | Name, email, plan, Stripe IDs |
| workspaces | /api/workspaces, /api/me | Domain, GTM container, status |
| api_keys | /api/workspaces, event ingestion | Per-workspace API key for script |
| alert_configs | /api/alert-config | Email, Slack webhook, thresholds |
bq mk --dataset --location=asia-south1 gtm_monitor then bq query --use_legacy_sql=false < tagdrishti-bigquery-schema.sql. Creates all 5 tables + 4 views + scheduled rollup.roles/bigquery.dataViewer on the gtm_monitor dataset. Cloud Run uses this to run SELECT queries for the dashboard endpoints.GOOGLE_CLOUD_PROJECT env var in Cloud Run. The BigQuery client picks this up automatically via ADC (Application Default Credentials) โ no separate key file needed on Cloud Run.bq-writer Cloud Function (included in schema SQL) subscribed to the gtm-monitor-events Pub/Sub topic. This writes inbound events to the correct BQ table by message_type.Table โ Dashboard page mapping
| BQ Table / View | Dashboard Page | Endpoint |
|---|---|---|
| v_tag_health_24h | Overview, Tag Health | /api/dashboard/overview, /api/tags |
| security_events | Security | /api/security |
| v_security_summary | Security stats | /api/security |
| v_consent_compliance | Consent page | /api/consent |
| web_vitals_rollups | Web Vitals page | /api/vitals |
| anomaly_alerts | Overview alert feed, Alerts page | /api/alerts, /api/dashboard/overview |
| v_daily_usage | BigQuery page usage bar, Settings plan | /api/bq/status |
ap-southeast-1 for India). Copy the REST URL and REST Token.UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN in Cloud Run.// Cache pattern in /api/dashboard/overview endpoint const cacheKey = `overview:${tenantId}:${workspaceId}:${range}`; const cached = await redis.get(cacheKey); if (cached) return JSON.parse(cached); // ... run BigQuery queries ... await redis.set(cacheKey, JSON.stringify(result), { ex: 60 }); // 60s TTL return result;
tagdrishti.com) under Domains and verify DNS records. Set RESEND_API_KEY and RESEND_FROM=alerts@tagdrishti.com.alert_configs.slack_webhook_url.anomaly_alert set, Cloud Run reads alert_config from Supabase for that workspace and fires both email (via Resend) and Slack (via HTTP POST to webhook). The notification_sent flag in anomaly_alerts BQ table tracks delivery.https://YOUR-CLOUD-RUN-URL/api/billing/webhook. Events: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted. Copy signing secret โ set as STRIPE_WEBHOOK_SECRET./api/billing/portal endpoint generate a self-serve portal URL so users can change plan, update card, or cancel from the dashboard.checkout.session.completed fires, Cloud Run reads the plan from metadata and updates tenants.plan in Supabase. The dashboard reads this from /api/me and shows the correct plan features in Settings.// โโ In TagDrishti-Dashboard-Full.html โโ // Find the CONFIG block near the top of the <script> section // Replace these 3 values: const CONFIG = { CLOUD_RUN_URL: 'https://gtm-monitor-api-YOUR-HASH.run.app', // โ your Cloud Run URL CLERK_PK: 'pk_live_YOUR_PUBLISHABLE_KEY', // โ from Clerk dashboard SUPABASE_URL: 'https://YOUR_PROJECT_REF.supabase.co', // โ only if direct BQ needed SUPABASE_ANON: 'YOUR_SUPABASE_ANON_KEY', // โ only if direct BQ needed USE_DEMO_DATA: false, // โ CHANGE THIS to false REFRESH_INTERVAL: 30000 };
USE_DEMO_DATA: false, fill in CLOUD_RUN_URL and CLERK_PK, and every API call in the dashboard switches from demo data to your live Cloud Run backend. All data flows, chart rendering, and table rendering are identical โ only the data source changes.Then add the Clerk JS SDK to the dashboard's <head> tag, implement the production initAuth() replacement shown in Section 4, and deploy to dashboard.tagdrishti.com (Vercel, Cloudflare Pages, or Firebase Hosting all work).
GET /health returns {"status":"ok","version":"7.4"}/api/me returns tenant record/api/bq/status returns status: "connected"USE_DEMO_DATA: false set. Deployed to dashboard.tagdrishti.com. Clerk auth redirects working.