{"openapi":"3.0.3","info":{"title":"ReviewFlow REST API","version":"1.0.0","description":"\n# ReviewFlow — Multi-Tenant AI Review Engine & Omnichannel Distribution Platform\n\nReviewFlow converts in-person customer visits into authentic 5-star Google Reviews via NFC/QR standees, dual LLM generation (Gemini & OpenAI), SMS/WhatsApp messaging, BullMQ background drip workers, and anti-review-gating compliant funnels.\n\n### Core Capabilities:\n- **Dual LLM Generation**: Fast, natural first-person Google review generation with deterministic fallbacks.\n- **Omnichannel Distribution**: High-error-correction QR code vector/raster generation, NFC NDEF links, Twilio SMS, and Meta WhatsApp Cloud API.\n- **POS Webhook Ingestion**: HMAC SHA-256 signed triggers with atomic Redis replay protection.\n- **Multi-Tenant Analytics**: Conversion funnel metrics across invited, opened, generated, copied, and redirected stages.\n- **Compliance**: FTC 16 CFR § 465 Anti-Gating compliance and TCPA/TRAI STOP opt-out suppression.\n    ","contact":{"name":"ReviewFlow Engineering Team","url":"https://github.com/cloudtrim/ai-review"}},"servers":[{"url":"http://localhost:3000","description":"Local Docker / Development Server"},{"url":"https://api.reviewflow.cloud","description":"Production Cluster"}],"tags":[{"name":"System & Health","description":"Subsystem healthcheck and monitoring"},{"name":"Review Engine","description":"AI review generation and status tracking"},{"name":"Customer Feedback","description":"Anti-gating compliant direct private feedback"},{"name":"Hardware & Distribution","description":"QR code generator and NFC URL builders"},{"name":"Messaging Dispatch","description":"Outbound Twilio SMS & Meta WhatsApp dispatch"},{"name":"POS Webhook Ingestion","description":"HMAC SHA-256 verified POS order trigger webhooks"},{"name":"Inbound Webhooks & Compliance","description":"Meta WhatsApp & Twilio inbound callbacks and opt-out handlers"},{"name":"Analytics & Reporting","description":"Multi-tenant conversion funnel analytics"}],"paths":{"/api/health":{"get":{"tags":["System & Health"],"summary":"System Healthcheck","description":"Real-time validation of PostgreSQL database connection, Redis broker, and Node.js process memory.","responses":{"200":{"description":"All subsystems are healthy and operational.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthStatus"},"example":{"status":"healthy","timestamp":"2026-09-04T12:00:00.000Z","version":"0.1.0","uptime":1420,"checks":{"database":{"status":"pass","latencyMs":2},"redis":{"status":"pass","latencyMs":1},"memory":{"status":"pass","latencyMs":0,"message":"Heap: 118MB, RSS: 324MB"}}}}}},"503":{"description":"One or more critical subsystems failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthStatus"}}}}}}},"/api/v1/reviews/generate":{"post":{"tags":["Review Engine"],"summary":"Generate AI Review","description":"Generates a personalized, persona-diversified review using Gemini 1.5 Flash / GPT-4o-mini, backed by semantic Redis template caching and PromptGuard sanitization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateReviewRequest"},"example":{"businessName":"Tasty Bites Mumbai","businessType":"restaurant","customerName":"Ajay More","rating":5,"tags":["Delicious Food","Quick Service"],"seoKeywords":["best butter chicken Mumbai"],"campaignId":"cmtmhryjh00024jo3jwyak1kc"}}}},"responses":{"200":{"description":"Review generated successfully.","headers":{"X-RateLimit-Limit":{"schema":{"type":"string"},"description":"Max requests per minute"},"X-RateLimit-Remaining":{"schema":{"type":"string"},"description":"Remaining requests in current window"},"X-RateLimit-Reset":{"schema":{"type":"string"},"description":"Unix timestamp of rate limit reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateReviewResponse"},"example":{"review":"Tasty Bites Mumbai exceeded all expectations! The butter chicken was rich, aromatic, and cooked to perfection. Staff was courteous and the service was lightning fast.","source":"llm","latencyMs":382,"sessionId":"cmtn0a1b2c3d4e5f6g7h8i9j0","googlePlaceUrl":"https://search.google.com/local/writereview?placeid=ChIJN1t_tDeuEmsRUsoyG83frY4"}}}},"400":{"description":"Validation failed or prompt injection detected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Invalid input detected"}}}},"404":{"description":"Campaign or business not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Campaign not found"}}}},"429":{"description":"Rate limit exceeded (Strict tier: 5 requests / min).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Too many requests. Please try again later."}}}}}}},"/api/v1/sessions/{id}/status":{"patch":{"tags":["Review Engine"],"summary":"Update Review Session Status","description":"Updates review session progression telemetry (e.g. COPIED, REDIRECTED).","parameters":[{"name":"id","in":"path","required":true,"description":"Review session CUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["PENDING","GENERATED","COPIED","REDIRECTED","FEEDBACK_SENT","EXPIRED"]}}},"example":{"status":"COPIED"}}}},"responses":{"200":{"description":"Status updated successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"status":{"type":"string"}}}}}}}}},"/api/v1/feedback/submit":{"post":{"tags":["Customer Feedback"],"summary":"Submit Private Management Feedback","description":"Direct customer feedback channel allowing users to report issues privately to business management (FTC Anti-Gating compliant).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitFeedbackRequest"},"example":{"campaignId":"cmtmhryjh00024jo3jwyak1kc","rating":2,"tags":["Slow Service","Wait Time"],"category":"service","message":"Waited over 35 minutes for our table and order was mixed up.","contactEmail":"customer@example.com"}}}},"responses":{"200":{"description":"Feedback recorded successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"feedbackId":{"type":"string"},"message":{"type":"string"}}},"example":{"success":true,"feedbackId":"fb_123456789","message":"Feedback submitted successfully"}}}},"400":{"description":"Validation error in submitted fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/qr/generate":{"post":{"tags":["Hardware & Distribution"],"summary":"Generate Branded QR Code","description":"Generates high-error-correction QR codes for physical table standees in vector SVG or high-resolution PNG with logo overlay.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["campaignSlug"],"properties":{"campaignSlug":{"type":"string","example":"tasty-bites-table-qr"},"format":{"type":"string","enum":["png","svg"],"default":"png"},"size":{"type":"integer","default":512,"example":600},"margin":{"type":"integer","default":2},"logoUrl":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"QR Code image stream.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string"}}}},"404":{"description":"Campaign slug not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/nfc/url":{"post":{"tags":["Hardware & Distribution"],"summary":"Build NFC NDEF Target URL","description":"Generates an NFC coaster/standee URL containing physical attribution UTM tags and NDEF record payload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["campaignSlug"],"properties":{"campaignSlug":{"type":"string","example":"tasty-bites-table-qr"}}}}}},"responses":{"200":{"description":"NFC URL and NDEF record instructions.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","example":"http://localhost:3000/r/tasty-bites-table-qr?utm_source=nfc&utm_medium=standee"},"recordType":{"type":"string","example":"url"}}}}}}}}},"/api/v1/sms/send":{"post":{"tags":["Messaging Dispatch"],"summary":"Send Direct SMS Review Invite","description":"Dispatches outbound SMS invite with mandatory STOP opt-out instructions via Twilio.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","customerName","businessName","reviewUrl"],"properties":{"to":{"type":"string","example":"+919876543210"},"customerName":{"type":"string","example":"Ajay More"},"businessName":{"type":"string","example":"Tasty Bites Mumbai"},"reviewUrl":{"type":"string","example":"http://localhost:3000/r/tasty-bites-table-qr"}}}}}},"responses":{"200":{"description":"SMS dispatched successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messageSid":{"type":"string"}}}}}}}}},"/api/v1/whatsapp/send":{"post":{"tags":["Messaging Dispatch"],"summary":"Send WhatsApp Template Review Invite","description":"Dispatches Meta WhatsApp Cloud API pre-approved template message.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","customerName","businessName","reviewUrl"],"properties":{"to":{"type":"string","example":"+919876543210"},"customerName":{"type":"string","example":"Ajay More"},"businessName":{"type":"string","example":"Tasty Bites Mumbai"},"reviewUrl":{"type":"string","example":"http://localhost:3000/r/tasty-bites-table-qr"},"templateName":{"type":"string","default":"review_invite"}}}}}},"responses":{"200":{"description":"WhatsApp template sent.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messageId":{"type":"string"}}}}}}}}},"/api/v1/campaigns/trigger":{"post":{"tags":["POS Webhook Ingestion"],"summary":"Trigger POS Inbound Order Webhook","description":"Secure webhook endpoint for POS billing systems. Enforces constant-time HMAC SHA-256 signature verification, timestamp clock drift checking, atomic Redis replay locks, and background BullMQ drip scheduling.","security":[{"hmacWebhookSignature":[]}],"parameters":[{"name":"X-Webhook-Signature","in":"header","required":true,"description":"HMAC SHA-256 hex digest of the raw body generated with WEBHOOK_HMAC_SECRET","schema":{"type":"string"}},{"name":"X-Webhook-Timestamp","in":"header","required":true,"description":"Current unix timestamp in milliseconds (must be within 5 minutes)","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerWebhookPayload"},"example":{"businessId":"cmtmhryiy00004jo3aoe6756a","campaignSlug":"tasty-bites-table-qr","customerName":"Aarav Sharma","customerPhone":"+919876543210","channel":"sms","idempotencyKey":"pos-inv-9823471"}}}},"responses":{"200":{"description":"Duplicate request acknowledged idempotently.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"202":{"description":"Webhook verified, review session created, and dispatch job enqueued.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"channel":{"type":"string"},"queued":{"type":"boolean"}}}}}},"401":{"description":"Missing signature, unconfigured secret, or expired timestamp."},"403":{"description":"Invalid HMAC signature."}}}},"/api/v1/webhooks/whatsapp":{"get":{"tags":["Inbound Webhooks & Compliance"],"summary":"Meta WhatsApp Webhook Verification Challenge","description":"Endpoint used by Meta Graph API to verify webhook subscription (`hub.challenge`).","parameters":[{"name":"hub.mode","in":"query","required":true,"schema":{"type":"string"}},{"name":"hub.verify_token","in":"query","required":true,"schema":{"type":"string"}},{"name":"hub.challenge","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Returns hub.challenge string"},"403":{"description":"Verification failed"}}},"post":{"tags":["Inbound Webhooks & Compliance"],"summary":"Inbound WhatsApp Event & Opt-Out Handler","description":"Receives customer replies. If customer replies \"STOP\" / \"UNSUBSCRIBE\", automatically updates `optedOutAt` in PostgreSQL and suppresses pending BullMQ follow-up jobs.","responses":{"200":{"description":"Event received and processed"}}}},"/api/v1/webhooks/twilio":{"post":{"tags":["Inbound Webhooks & Compliance"],"summary":"Inbound Twilio SMS Opt-Out Handler","description":"Receives incoming SMS messages. Auto-suppresses future drips when customer replies \"STOP\".","responses":{"200":{"description":"TwiML response returned"}}}},"/api/v1/analytics/funnel":{"get":{"tags":["Analytics & Reporting"],"summary":"Get Multi-Tenant Funnel Metrics","description":"Aggregates stage counts (invited, opened, generated, copied, redirected, feedbackSent), conversion percentages, channel breakdown, and daily trends.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"businessId","in":"query","description":"Optional business ID filter (must match authenticated tenant)","schema":{"type":"string"}},{"name":"campaignId","in":"query","description":"Optional campaign ID filter","schema":{"type":"string"}},{"name":"from","in":"query","description":"Start date in ISO 8601 format","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"End date in ISO 8601 format","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Funnel analytics metrics computed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelMetrics"}}}},"401":{"description":"Unauthorized. Missing API key or bearer token."},"403":{"description":"Forbidden. Cross-tenant access attempted."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Business API Key or Admin Secret Token"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Custom X-API-Key tenant header"},"hmacWebhookSignature":{"type":"apiKey","in":"header","name":"X-Webhook-Signature","description":"HMAC SHA-256 signature generated with shared secret"}},"schemas":{"HealthStatus":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","degraded","unhealthy"]},"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string"},"uptime":{"type":"integer"},"checks":{"type":"object","properties":{"database":{"$ref":"#/components/schemas/ComponentHealth"},"redis":{"$ref":"#/components/schemas/ComponentHealth"},"memory":{"$ref":"#/components/schemas/ComponentHealth"}}}}},"ComponentHealth":{"type":"object","properties":{"status":{"type":"string","enum":["pass","fail"]},"latencyMs":{"type":"integer"},"message":{"type":"string"}}},"GenerateReviewRequest":{"type":"object","required":["businessName","businessType","customerName","rating","tags","campaignId"],"properties":{"businessName":{"type":"string","maxLength":100},"businessType":{"type":"string","maxLength":50},"customerName":{"type":"string","maxLength":100},"rating":{"type":"integer","minimum":1,"maximum":5},"tags":{"type":"array","items":{"type":"string"}},"seoKeywords":{"type":"array","items":{"type":"string"},"default":[]},"campaignId":{"type":"string"},"language":{"type":"string","enum":["en","hi","hinglish","mr","gu","ta","te","kn","bn","pa","ml"],"default":"en","description":"Target Indian regional language or dialect"}}},"GenerateReviewResponse":{"type":"object","properties":{"review":{"type":"string"},"source":{"type":"string","enum":["llm","fallback"]},"latencyMs":{"type":"integer"},"sessionId":{"type":"string"},"googlePlaceUrl":{"type":"string"}}},"SubmitFeedbackRequest":{"type":"object","required":["campaignId","rating","tags","category","message"],"properties":{"campaignId":{"type":"string"},"rating":{"type":"integer","minimum":1,"maximum":5},"tags":{"type":"array","items":{"type":"string"}},"category":{"type":"string"},"message":{"type":"string"},"contactEmail":{"type":"string","format":"email"},"sessionId":{"type":"string"}}},"TriggerWebhookPayload":{"type":"object","required":["businessId","campaignSlug","customerName","customerPhone","channel"],"properties":{"businessId":{"type":"string"},"campaignSlug":{"type":"string"},"customerName":{"type":"string"},"customerPhone":{"type":"string","pattern":"^\\+[1-9]\\d{1,14}$"},"channel":{"type":"string","enum":["sms","whatsapp"]},"idempotencyKey":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"type":"string"}}}},"FunnelMetrics":{"type":"object","properties":{"period":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}}},"businessId":{"type":"string"},"campaignId":{"type":"string"},"invited":{"type":"integer"},"opened":{"type":"integer"},"generated":{"type":"integer"},"copied":{"type":"integer"},"redirected":{"type":"integer"},"feedbackSent":{"type":"integer"},"conversionRates":{"type":"object","properties":{"invitedToOpened":{"type":"number"},"openedToGenerated":{"type":"number"},"generatedToCopied":{"type":"number"},"copiedToRedirected":{"type":"number"},"overallConversion":{"type":"number"}}},"channelBreakdown":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"count":{"type":"integer"},"redirected":{"type":"integer"},"conversionRate":{"type":"number"}}}},"dailyTrend":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"invited":{"type":"integer"},"redirected":{"type":"integer"}}}}}},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"object"}}}}}}