Developer Quick Reference & Handover Guide
A comprehensive reference for developers working with or taking over the GP3MixMedia TrailMix platform.
Project Overview
| Attribute | Value |
|---|
| Type | Political Campaign Media Tracking Platform |
| Framework | Next.js 15.5.9 (App Router) |
| Runtime | Node.js 22.14.0 |
| Database | MySQL 8+ with Prisma 7.2.0 |
| Language | TypeScript 5.9 / React 19.2 |
| Deployment | Fly.io (Docker containers) |
| Cache | Upstash Redis (REST API) |
| Storage | AWS S3 / Tigris (S3-compatible) |
| Email | Gmail API (OAuth 2.0) |
| Monitoring | Sentry + OpenTelemetry + Prometheus |
Key Statistics
| Metric | Count |
|---|
| Database Models | 221 (Prisma schema) |
| API Endpoints | 286 |
| Frontend Pages | 42+ |
| External Integrations | 15+ |
| Scheduled Jobs | 25 |
| Feed Sources | 9 |
| Test Files | 331+ |
Directory Structure
gp3mixmedia/
├── gp3mixmedia-web/ # Main application
│ ├── app/ # Next.js App Router
│ │ ├── api/v1/ # 286 REST API endpoints
│ │ ├── (docs)/docs/ # In-app documentation portal
│ │ ├── campaigns/ # Campaign pages
│ │ ├── ingestion/ # Ingestion pages
│ │ ├── dedupe/ # Deduplication pages
│ │ └── admin/ # Admin pages
│ ├── src/
│ │ ├── workers/ # Background jobs & scheduler
│ │ │ ├── feeds/ # Feed implementations (FCC, AdImpact, Meta, etc.)
│ │ │ ├── mailbox/ # Gmail polling (rep sheets, newsletters)
│ │ │ ├── scheduler/ # Cron job configuration
│ │ │ └── index.ts # Worker entry point
│ │ └── server/ # Server-side utilities
│ ├── lib/ # Shared libraries & services
│ │ ├── services/ # Business logic (alerts, dedupe, reconciliation)
│ │ └── vendor/ # External API clients (FCC, OpenFEC)
│ ├── components/ # React UI components
│ ├── prisma/
│ │ ├── schema.prisma # Database schema (221 models)
│ │ └── migrations/ # 153 schema migrations
│ ├── content/docs/ # In-app documentation (markdown files)
│ ├── tests/ # Test files (331+ files, Vitest)
│ ├── .env.example # Complete env var reference
│ ├── Dockerfile # Multi-stage production build
│ └── fly.toml # Fly.io web app config
├── deploy/
│ ├── mysql/ # MySQL + ProxySQL Docker setup
│ │ ├── Dockerfile # MySQL 8.0.44 image
│ │ └── fly.toml # Fly.io DB config
│ └── fly/ # Deployment helpers
├── docs/ # Extended documentation
│ ├── api/ # API docs (auth, pagination, errors)
│ ├── adr/ # Architecture Decision Records
│ ├── runbooks/ # Incident response procedures
│ └── email-parsing/ # Email parsing system docs
├── fly.toml # Fly.io database config
└── README.md # Project overview
External Dependencies
Infrastructure Services
| Service | Purpose | Required? | Notes |
|---|
| Fly.io | Hosting (web + workers + DB) | Yes | 3 process types: web, ingest, historical-ingest |
| MySQL 8+ | Primary database | Yes | Hosted on Fly.io with ProxySQL multiplexing |
| Upstash Redis | Rate limiting, caching | Yes (prod) | REST-based Redis; falls back to in-memory in dev |
| AWS S3 / Tigris | File storage (uploads, creatives, PDFs) | Yes | S3-compatible; Tigris used on Fly.io |
| Docker | Containerization | Yes (deploy) | Multi-stage Dockerfile for app; custom MySQL image |
| GitHub | Source control | Yes | Standard git workflow |
Data Source APIs
| Service | Purpose | Required? | API Key Variable |
|---|
| Meta Ad Library | Facebook/Instagram political ads | Optional | META_ADS_API_TOKEN |
| Google BigQuery | Google/YouTube political ads | Optional | GOOGLE_CLOUD_PROJECT_ID + credentials |
| AdImpact | Industry ad intelligence | Optional | ADIMPACT_API_TOKEN |
| OpenFEC | Federal campaign finance data | Optional | OPENFEC_API_KEY |
| FEC 24-Hour | Independent expenditure filings | Optional | FEC_API_KEY |
| FCC | Broadcast ad disclosures | Optional | No key needed (HTML scraping) |
| Google Civic API | Ballot info, candidates | Optional | GOOGLE_CIVIC_API_KEY |
Communication & Monitoring
| Service | Purpose | Required? | Config Variable |
|---|
| Gmail API | Email sending + mailbox polling | Yes | GMAIL_OAUTH_* + INGESTION_GMAIL_OAUTH_* |
| Sentry | Error tracking | Recommended | SENTRY_DSN |
| OpenTelemetry | Distributed tracing | Optional | OTEL_EXPORTER_OTLP_ENDPOINT |
| Speechmatics | Video transcription | Optional | SPEECHMATICS_API_KEY |
| Deepgram | Video transcription (alt) | Optional | DEEPGRAM_API_KEY |
| Google Custom Search | Web search enrichment | Optional | GOOGLE_CUSTOM_SEARCH_API_KEY |
Fly.io Architecture
The platform runs as two separate Fly.io apps:
1. Database App (gp3mixmedia)
- Image: Custom MySQL 8.0.44 + ProxySQL
- Region:
iad (US East)
- Ports: 3306 (MySQL direct), 6033 (ProxySQL - app should connect here)
- Storage: 8GB persistent volume at
/var/lib/mysql
- VM: 8GB RAM, 4 performance CPUs
2. Web App (gp3mixmedia-web)
- Region:
iad (US East)
- Processes:
web — Next.js server (port 3000, HTTPS enforced)
ingest — Background ingestion workers (cron-based feeds, Gmail polling)
historical-ingest — Large backfill jobs (auto-suspends when idle)
- VM per process: 8GB RAM, 1-2 performance CPUs
- Deploy:
fly deploy runs migrations via release_command
- Health checks:
/api/v1/health (45s timeout), /api/v1/ping (10s timeout)
Deployment Commands
# Deploy web app
cd gp3mixmedia-web && fly deploy
# Deploy database
cd deploy/mysql && fly deploy --app gp3mixmedia
# Set secrets (web app)
fly secrets set META_ADS_API_TOKEN=xxx --app gp3mixmedia-web
# SSH into web app
fly ssh console --app gp3mixmedia-web
# View logs
fly logs --app gp3mixmedia-web
fly logs --app gp3mixmedia-web --process ingest
# Scale machines
fly scale count web=1 ingest=1 --app gp3mixmedia-web
Environment Variables
Required Secrets (set via fly secrets set or .env)
These have no defaults and must be provided:
| Variable | Purpose |
|---|
DATABASE_URL | MySQL connection string (mysql://USER:PASS@HOST:PORT/DB) |
NEXTAUTH_SECRET | Session encryption key (generate: openssl rand -base64 32) |
AUTH_SECRET | Alternative auth secret (same purpose as NEXTAUTH_SECRET) |
AUTH_GOOGLE_CLIENT_ID | Google OAuth client ID for login |
AUTH_GOOGLE_CLIENT_SECRET | Google OAuth client secret |
CSRF_SECRET | CSRF token signing (generate: openssl rand -base64 32) |
TWO_FA_ENCRYPTION_KEY | 2FA TOTP encryption (min 32 chars) |
GMAIL_OAUTH_CLIENT_ID | Gmail API OAuth client ID (email sending) |
GMAIL_OAUTH_CLIENT_SECRET | Gmail API OAuth client secret |
GMAIL_OAUTH_REFRESH_TOKEN | Gmail API refresh token |
GMAIL_FROM_EMAIL | Sender email address |
INGESTION_GMAIL_OAUTH_CLIENT_ID | Gmail API OAuth for mailbox polling |
INGESTION_GMAIL_OAUTH_CLIENT_SECRET | Gmail API OAuth secret for polling |
INGESTION_GMAIL_OAUTH_REFRESH_TOKEN | Gmail API refresh token for polling |
AWS_ACCESS_KEY_ID | S3 access key |
AWS_SECRET_ACCESS_KEY | S3 secret key |
S3_BUCKET | S3 bucket name |
UPSTASH_REDIS_REST_URL | Upstash Redis REST endpoint (HTTPS) |
UPSTASH_REDIS_REST_TOKEN | Upstash Redis auth token |
REP_LABEL_ID | Gmail Label ID for rep/buyline messages (use label-utils to find) |
NEWSLETTER_LABEL_ID | Gmail Label ID for newsletter messages |
Required for Specific Feeds
| Variable | Feed | Notes |
|---|
META_ADS_API_TOKEN | Meta Ads Library | Facebook access token (see Meta Feed Setup below) |
GOOGLE_CLOUD_PROJECT_ID | Google Political Ads | GCP project with BigQuery access |
GOOGLE_CLOUD_CREDENTIALS_JSON | Google Political Ads | Inline GCP service account JSON |
OPENFEC_API_KEY | OpenFEC | Get at api.open.fec.gov |
FEC_API_KEY | FEC 24-Hour Reports | Same API, separate key recommended |
ADIMPACT_API_TOKEN | AdImpact | Provided by AdImpact |
ADIMPACT_ENCRYPTION_KEY | AdImpact | For encrypting stored credentials |
GOOGLE_CIVIC_API_KEY | Google Civic | For ballot/candidate lookup |
SPEECHMATICS_API_KEY | Transcription | For video transcription |
VAPID_PUBLIC_KEY | Push Notifications | Generate with web-push generate-vapid-keys |
VAPID_PRIVATE_KEY | Push Notifications | Generate with web-push generate-vapid-keys |
Variables with Defaults (override if needed)
| Variable | Default | Purpose |
|---|
NEXTAUTH_URL | http://localhost:3000 | Base URL for auth |
NEXT_PUBLIC_APP_URL | http://localhost:3000 | Public-facing URL |
APP_ENV | development | App environment |
NODE_ENV | development | Node environment |
TZ | America/New_York | Server timezone |
LOG_LEVEL | info | Log verbosity (debug, info, warn, error) |
EMAIL_FROM_NAME | GP3 Mix Media | Email sender display name |
EMAIL_DEFAULT_SEND_MODE | queued | Email sending mode |
GMAIL_DAILY_SEND_LIMIT | 2000 | Daily Gmail send cap |
WORKER_POOL_SIZE | 4 | DB pool size for worker processes |
POOL_CPU_MULTIPLIER | 2 | DB pool = CPUs x multiplier |
VAPID_SUBJECT | mailto:[email protected] | Push notification identity |
PROMETHEUS_PORT | 9090 | Metrics port |
RATE_LIMIT_STRICT | true (prod) | Reject requests when Redis down |
DLQ_ENABLED | true | Dead letter queue |
DLQ_MAX_RETRIES | 3 | DLQ retry attempts |
DLQ_RETENTION_DAYS | 30 | DLQ record retention |
Variables Set in the UI (Campaign Settings)
These are configured per-campaign through the web interface, not env vars:
| Setting | Location | Purpose |
|---|
| FCC DMAs/Markets | Campaign Settings > Feeds | Which markets to monitor for FCC filings |
| FCC Advertiser Keywords | Campaign Settings > Feeds | Names as they appear in FCC filings |
| FCC Regex Patterns | Campaign Settings > Feeds | Custom matching rules |
| AdImpact/AIDE Webhook URL | Campaign Settings > Feeds | AIDE integration endpoint |
| AdImpact/AIDE Tokens | Campaign Settings > Feeds | Webhook authentication |
| OpenFEC Committee ID | Campaign Settings > Feeds | FEC Committee ID (format: C00XXXXXX) |
| Meta Search Terms | Campaign Settings > Feeds | Keywords to find relevant Meta ads |
| Meta Advertiser IDs | Campaign Settings > Feeds | Specific Meta advertiser accounts |
| Google Ads Config | Campaign Settings > Feeds | Advertiser discovery settings |
| Alert Thresholds | Campaign Settings > Alerts | Spend change notification triggers |
| Email Recipients | Campaign Settings > Alerts | Alert delivery addresses |
| Mailbox Routing Rules | Campaign Settings > Feeds | Email pattern matching rules |
| EOD Report Schedule | Campaign Settings > Reports | End-of-day report timing |
Scheduled Jobs (Crons)
Feed Ingestion Schedules
| Feed | Default Cron | Env Override | Description |
|---|
| AdImpact | 15 * * * * | ADIMPACT_CRON | Hourly at :15 |
| FCC Political Files | 45 */6 * * * | FCC_CRON | Every 6 hours at :45 |
| Google Political Ads | 0 6,14,22 * * * | GOOGLE_POLITICAL_ADS_CRON | 3x daily (6 AM, 2 PM, 10 PM) |
| FEC 24-Hour Reports | 0 */4 * * * | FEC_24H_CRON | Every 4 hours |
| Meta Ads Library | 0 */8 * * * | META_ADS_CRON | Every 8 hours |
| State Press RSS | 0 */6 * * * | STATE_PRESS_RSS_CRON | Every 6 hours |
| Feed Retry Queue | */10 * * * * | FEED_RETRY_PROCESS_CRON | Every 10 minutes |
Background Worker Schedules
| Job | Default Cron | Enabled Flag | Description |
|---|
| Email Queue | */5 * * * * | EMAIL_QUEUE_ENABLED (true) | Process queued emails |
| Pre-Buy Alerts | */10 * * * * | PRE_BUY_ALERTS_ENABLED (true) | Process alert triggers |
| Social Creative Discovery | 5,35 * * * * | SOCIAL_CREATIVE_DISCOVERY_ENABLED (true) | Find social ad creatives |
| Creative Followup | 10,40 * * * * | CREATIVE_FOLLOWUP_ENABLED (true) | Follow up on creatives |
| Social Monitor (X/Twitter) | 15,45 * * * * | SOCIAL_MONITOR_ENABLED (true) | Monitor social feeds |
| Radio Creative Discovery | 20,50 * * * * | RADIO_CREATIVE_DISCOVERY_ENABLED (true) | Find radio creatives |
| AdImpact Creative Download | 25,55 * * * * | ADIMPACT_CREATIVE_DOWNLOAD_ENABLED (true) | Download ad creatives |
| AdImpact Transcription | 10 * * * * | ADIMPACT_CREATIVE_TRANSCRIBE_ENABLED (true) | Transcribe creatives |
| FCC Coverage Monitoring | 25 * * * * | FCC_COVERAGE_MONITORING_ENABLED (true) | Verify FCC coverage |
| Station Refresh | 30 7 * * * | STATION_REFRESH_ENABLED (true) | Refresh station rosters |
| EOD Reports | 0 * * * * | EOD_REPORT_ENABLED (true) | Process EOD reports |
| EOD Scheduling | 0 2 * * * | EOD_SCHEDULING_ENABLED (true) | Pre-generate EOD reports |
| Candidate Roster | 0 6 * * * | CANDIDATE_ROSTER_MONITOR_ENABLED (true) | Check Ballotpedia rosters |
| Dashboard Stats | */7 * * * * | DASHBOARD_STATS_ENABLED (true) | Aggregate dashboard metrics |
| Discovery Refresh | 0 2 * * 0 | DISCOVERY_REFRESH_ENABLED (true) | Weekly discovery refresh |
| AdImpact Reconciliation | 0 2 * * 0 | ADIMPACT_FULL_RECONCILIATION_ENABLED (true) | Weekly full resync |
| Dedupe Auto-Resolver | 0 */2 * * * | DEDUPE_AUTO_RESOLVER_ENABLED (false) | Auto-resolve high-confidence |
| Parser Success Monitoring | 15 8 * * * | PARSER_SUCCESS_RATE_MONITORING_ENABLED (true) | Daily parser health |
Health & Monitoring Schedules
| Job | Default Cron | Enabled Flag | Description |
|---|
| DLQ Auto-Dismiss | 30 */4 * * * | DLQ_AUTO_DISMISS_ENABLED (true) | Auto-dismiss stale DLQ |
| DLQ Cleanup | 0 3 * * * | DLQ_CLEANUP_ENABLED (true) | Clean old DLQ records |
| DLQ Monitor | */15 * * * * | DLQ_MONITOR_ENABLED (true) | Monitor DLQ health |
| Queue Health Check | */15 * * * * | QUEUE_HEALTH_CHECK_ENABLED (true) | Check queue health |
| Stale Watermark Monitor | */30 * * * * | STALE_WATERMARK_MONITOR_ENABLED (true) | Detect stale feeds |
| Email Ingestion Watchdog | */2 * * * * | EMAIL_INGESTION_WATCHDOG_ENABLED (true) | Email polling health |
| MySQL Connection Monitor | * * * * * | MYSQL_CONNECTION_MONITOR_ENABLED (true) | DB connection limits |
Polling Workers (non-cron)
| Worker | Default Interval | Env Override | Description |
|---|
| Gmail Polling (reps) | 60s | INGESTION_GMAIL_POLL_INTERVAL | Poll for rep emails |
| Newsletter Polling | 300s (5 min) | NEWSLETTER_GMAIL_POLL_INTERVAL | Poll for newsletters |
| IMAP Polling | 60s | INGESTION_MAIL_POLL_INTERVAL | Poll IMAP inbox |
| Input Parse | 15s | INPUT_PARSE_POLL_INTERVAL | Process uploaded files |
| RSS Feeds | 300s | RSS_POLL_INTERVAL | Poll RSS feeds |
| S3 Recovery | 30s | S3_RECOVERY_POLL_INTERVAL | Retry failed S3 uploads |
The Meta Ads Library feed pulls political/issue ads from Facebook and Instagram.
Prerequisites
- Facebook Developer Account with access to the Ad Library API
- Facebook Access Token with
ads_read permission
- Go to Meta for Developers
- Create an app or use an existing one
- Generate a long-lived access token with
ads_read scope
- The token must belong to an account approved for the Ad Library API
Environment Configuration
Set the access token as a secret:
# On Fly.io
fly secrets set META_ADS_API_TOKEN=your_facebook_access_token --app gp3mixmedia-web
# In .env for local development
META_ADS_API_TOKEN=your_facebook_access_token
The following env vars have sensible defaults but can be overridden:
| Variable | Default | Purpose |
|---|
META_ADS_CRON | 0 */8 * * * | Feed schedule (every 8 hours) |
META_ADS_API_VERSION | v20.0 | Graph API version |
META_ADS_PER_PAGE | 100 | Results per API page (max 250) |
META_ADS_MAX_PAGES | 10 | Max pages per search term |
META_ADS_SEARCH_COUNTRIES | US | Country filter |
META_ADS_AD_TYPE | POLITICAL_AND_ISSUE_ADS | Ad type filter |
META_ADS_MAX_RETRIES | 3 | Retry attempts (max 10) |
META_ADS_RETRY_DELAY_MS | 2000 | Base retry delay |
META_ADS_REQUEST_TIMEOUT_MS | 60000 | Request timeout (max 120s) |
META_ADS_INTER_REQUEST_DELAY_MS | 1000 | Delay between requests |
META_ADS_LOOKBACK_DAYS | 21 | Minimum lookback window |
META_ADS_ALLOW_KEYWORD_FALLBACK | true | Fall back to keyword search |
META_ADS_ALLOW_AUTO_DERIVE_TERMS | true | Auto-derive search terms from campaign |
META_ADS_REDUCED_FIELDS | false | Use lite field set (omits demographics) |
Campaign-Level Configuration
After the env var is set, configure per-campaign in the UI:
- Navigate to Campaign Settings > Feeds > Meta Ads Library
- Configure:
- Search Terms: Keywords to find relevant ads (leave empty for auto-derive from campaign name/candidates)
- Advertiser IDs: Optional — specific Meta advertiser page IDs to monitor
- Geographic Scope: Countries/regions to include
How It Works
- The feed runs on the
META_ADS_CRON schedule (default: every 8 hours)
- For each campaign with Meta enabled:
- Uses search terms (configured or auto-derived from campaign candidates)
- Queries the Meta Ad Library API for political/issue ads matching those terms
- Applies keyword fallback if primary search returns few results
- Converts matching ads to buy line candidates
- Runs deduplication against existing records
- Circuit breaker protects against API failures with exponential backoff
- Partial results are saved on timeouts (graceful degradation)
Key Files
- Feed implementation:
src/workers/feeds/meta-ads-library.ts
- API client:
lib/services/social-creative/meta-ads-library.client.ts
Local Development Setup
Prerequisites
- Node.js 22.14.0+ (use
nvm or fnm)
- MySQL 8+ (local or Docker)
- Redis (optional for dev — falls back to in-memory)
Quick Start
# 1. Clone and install
git clone <repo-url> && cd gp3mixmedia/gp3mixmedia-web
npm install
# 2. Set up environment
cp .env.example .env
# Edit .env with your DATABASE_URL, auth secrets, etc.
# 3. Set up database
npx prisma migrate deploy # Apply all migrations
npx prisma generate # Generate Prisma client
# 4. Start development
npm run dev # Next.js dev server on :3000
# 5. Start workers (separate terminal)
npm run ingest # Background workers
Common Commands
# Development
npm run dev # Start dev server
npm run ingest # Start ingestion workers
npm run build # Production build
npm start # Production server
# Database
npx prisma migrate deploy # Apply migrations
npx prisma migrate dev # Create new migration
npx prisma studio # Visual database browser
npx prisma generate # Regenerate client after schema changes
# Testing
npm test # Run all tests (Vitest)
npm run test:watch # Watch mode
npm run build # Verify no build errors (important for circular deps)
# Gmail Label Discovery
npx tsx src/workers/mailbox/label-utils.ts # List Gmail label IDs
# Deployment
fly deploy --app gp3mixmedia-web # Deploy web app
fly secrets set KEY=VALUE --app gp3mixmedia-web # Set secrets
API Endpoints Quick Reference
Core
GET/POST /api/v1/campaigns
GET/PATCH /api/v1/campaigns/{id}
POST /api/v1/campaigns/{id}/clone
Matrix
GET /api/v1/matrix
POST /api/v1/matrix/batch-update
GET /api/v1/matrix/export
Deduplication
GET /api/v1/dedupe/groups
POST /api/v1/dedupe/groups/{id}/resolve
POST /api/v1/dedupe/rows/combine
Ingestion
GET/POST /api/v1/ingestion/control
POST /api/v1/aide/inbound # AIDE webhook endpoint
Email & Notifications
POST /api/v1/email/send
GET /api/v1/email/logs
POST /api/v1/push/subscribe
POST /api/v1/push/send
Health & Monitoring
GET /api/v1/ping # Liveness check
GET /api/v1/health # Full health check
GET /api/v1/observability/metrics # Prometheus metrics
GET /api/v1/circuit-breakers # Circuit breaker status
Interactive API Docs
- Swagger UI:
/api/docs
- OpenAPI Spec:
/api/v1/openapi.json
Key Design Patterns
Circular Dependency Prevention
Next.js builds can fail with ReferenceError: Cannot access 'X' before initialization due to circular imports. Always use dynamic imports in API route files for service/worker modules:
// WRONG — static import causes TDZ errors
import { someService } from '@/lib/services/some-service';
// RIGHT — dynamic import breaks circular chain
async function getSomeService() {
const { someService } = await import('@/lib/services/some-service');
return someService;
}
Soft Deletes
High-value tables use soft deletes (archived, not deleted). Data can always be recovered from the archives.
Feed Distributed Locks
Redis-based locks prevent duplicate feed runs across processes. Lock TTL defaults to 2700s with 60s heartbeat.
Circuit Breaker
External API calls use circuit breakers for resilience. Status viewable at /api/v1/circuit-breakers.
Quick Troubleshooting
| Issue | Solution |
|---|
| Build fails with TDZ error | Use dynamic imports in API routes (see pattern above) |
| Tests failing | Run npx prisma generate then npm test |
| Database connection error | Check DATABASE_URL format; ensure MySQL is running |
| Missing Prisma client | Run npx prisma generate |
| S3 upload fails | Check AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET |
| Push notifications broken | Check VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY |
| Rate limiting issues | Verify UPSTASH_REDIS_REST_URL and token |
| Gmail polling not starting | Check REP_LABEL_ID / NEWSLETTER_LABEL_ID are set |
| Meta feed not running | Verify META_ADS_API_TOKEN is set; check token expiry |
| Feed stuck / locked | Check Redis lock status; feed locks expire after 45 min |
Security Features
- Google Workspace SSO (NextAuth.js)
- Role-based access control (Viewer, Analyst, Admin) with per-campaign permissions
- CSRF protection with signed tokens
- Optional 2FA (TOTP authenticator apps)
- API rate limiting via Upstash Redis (configurable per tier)
- Request idempotency to prevent duplicate processing
- Soft-delete with recovery on all high-value data
- AES-256-GCM encryption for stored API tokens
- Comprehensive audit trails
- Sentry error tracking and alerting
- Circuit breaker pattern for external API resilience
Monitoring Endpoints
| Endpoint | Purpose |
|---|
/api/v1/ping | Liveness check (fast) |
/api/v1/health | Full health check (DB, Redis, S3) |
/api/v1/observability/metrics | Prometheus metrics |
/api/v1/circuit-breakers | Circuit breaker status |
/api/v1/service-health | External service status |
Last Updated: March 2026
Was this helpful? If you have feedback or questions, please contact your administrator.