{"openapi":"3.0.0","info":{"title":"Market Voice — Agent API","version":"1.0.0","description":"Endpoints for Salesforce Agentforce to ground Agent replies with live automotive VOC data (social posts, KOLs, competitor signals, predictive trends)."},"servers":[{"url":"https://market-voice.vercel.app"}],"paths":{"/api/agent/query-voc":{"post":{"operationId":"queryVoc","summary":"Query VOC data for a brand, campaign topic, and market","description":"Returns social post totals, top negative posts, top KOLs, competitor signals, and predictive negative-share trend for the selected campaign. Agent should use `summary` for a one-line answer and `topNegativePosts` for grounding. Sentiment shares use classified discussions only; missing and unrecognized sentiment are unknown, references are excluded, and zero classified denominator returns null. Predictive fields have separate eligibility rules.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryVocRequest"}}}},"responses":{"200":{"description":"VOC context","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryVocResponse"}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"QueryVocRequest":{"type":"object","properties":{"brand":{"type":"string","description":"Optional brand name. Topic-only Taiwan requests default to Mercedes-Benz."},"market":{"type":"string","description":"Optional market code, e.g. CN, TW, GLOBAL"},"days":{"type":"integer","description":"Lookback window in days (1-3650). Keyword searches default to full history.","minimum":1,"maximum":3650,"default":7},"startDate":{"type":"string","format":"date","description":"Optional inclusive start date in YYYY-MM-DD format. Takes precedence over days."},"endDate":{"type":"string","format":"date","description":"Optional inclusive end date in YYYY-MM-DD format. Defaults to the current date."},"topic":{"type":"string","description":"Campaign or keyword filter matched against post content, model name, and voice type."},"sentiment":{"type":"string","enum":["positive","negative","neutral"],"description":"Optional sentiment filter for the topNegativePosts list. Defaults to negative."}}},"QueryVocResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"found":{"type":"boolean"},"brand":{"type":"string"},"market":{"type":"string"},"windowDays":{"type":"integer"},"windowStart":{"type":"string","format":"date-time"},"windowEnd":{"type":"string","format":"date-time"},"summary":{"type":"string","description":"One-line human-readable summary. Agent should quote this."},"totals":{"type":"object","properties":{"posts":{"type":"integer"},"positive":{"type":"integer"},"negative":{"type":"integer"},"neutral":{"type":"integer"},"unknown":{"type":"integer"},"classified":{"type":"integer"},"metricVersion":{"type":"string","example":"mv-sentiment-v1"},"negSharePct":{"type":"number","nullable":true,"description":"Negative / classified * 100, or null if classified is zero."},"posSharePct":{"type":"number","nullable":true,"description":"Positive / classified * 100, or null if classified is zero."}}},"topNegativePosts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string"},"title":{"type":"string"},"author":{"type":"string","nullable":true},"authorHandle":{"type":"string"},"likes":{"type":"integer"},"comments":{"type":"integer"},"shares":{"type":"integer"},"views":{"type":"integer"},"publishedAt":{"type":"string"},"url":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"voiceType":{"type":"string","nullable":true},"sentiment":{"type":"string","nullable":true},"content":{"type":"string"}}}},"matchedPostCount":{"type":"integer"},"matchedPosts":{"type":"array","description":"Raw posts matching the topic, including unclassified posts.","items":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string"},"title":{"type":"string"},"author":{"type":"string","nullable":true},"authorHandle":{"type":"string","nullable":true},"likes":{"type":"integer"},"comments":{"type":"integer"},"shares":{"type":"integer"},"views":{"type":"integer"},"publishedAt":{"type":"string"},"url":{"type":"string","nullable":true},"sentiment":{"type":"string","nullable":true},"content":{"type":"string"}}}},"classifiedPostCount":{"type":"integer"},"unclassifiedPostCount":{"type":"integer"},"dataQualityNote":{"type":"string"},"channelStats":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"posts":{"type":"integer"},"likes":{"type":"integer"},"comments":{"type":"integer"},"shares":{"type":"integer"},"views":{"type":"integer"}}}},"topKols":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"followers":{"type":"integer"},"channel":{"type":"string"},"sentiment":{"type":"string","nullable":true},"url":{"type":"string","nullable":true},"excerpt":{"type":"string"}}}},"recentSignals":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"headline":{"type":"string"},"detail":{"type":"string","nullable":true},"severity":{"type":"string"},"detectedAt":{"type":"string"}}}},"predictiveVerdict":{"type":"string","enum":["rising_high","rising_medium","stable","falling_medium","falling_high","insufficient_data"]},"predictiveDetail":{"type":"object","nullable":true,"properties":{"currentNegPct":{"type":"number"},"forecast7dPct":{"type":"number"},"jumpPct":{"type":"number"}}}}}}},"security":[{"bearerAuth":[]}]}