Skip to main content
Documentation

Reviews

reviews.insights

Read the shop's review score, response rate and overdue replies
Read-onlyRead budget

REST

Shipping
POST /api/v1/reviews/insights

MCP tool

Live
reviews.insights

Exposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Reviews domain.

Operating contract

Every headline number about the shop's reviews in one pass — the same arithmetic the hub's KPI strip and the weekly digest read, so this and the screen cannot disagree.

average_rating is the mean of the reviews this product has synced, which may cover less history than the average Google itself displays. Say 'across the reviews we have' rather than quoting it as the shop's Google score.

overdue IS THE FINDING. It counts reviews that are unanswered and past the target the shop set for their star band — a one-star has a much tighter target than a five-star, deliberately. oldest_overdue_hours is how long the worst one has been waiting, and it is the number worth saying out loud.

awaiting_auto_reply is different and must not be added to overdue: those are unanswered reviews the shop's own Review Responder is still inside its delay window for. Nobody needs to act on them yet.

response_rate and sla_compliance_rate are 0-1 and null when there is nothing to measure. A null is 'we cannot say', not zero.

rating_only is reviews with a score and no words. They drag the response rate down and there is nothing to answer, which is worth mentioning before an owner is told they are behind.

This is a rollup over every synced review, so nothing is truncated.

Who may call it

Permission
marketing.accessThe caller must hold Reviews & market at the ACT level. A read-only dashboard grant on the same section is refused.
Plan
Every planNo plan gate. Available on every Service VIN plan.
Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.
Rate class
readCounted against the read budget — the widest of the four.

Input

This capability takes no arguments.

Output

FieldTypeDescription
totalnumber

average_ratingnumber | null

distributionobject[]

distribution[].starsnumber

distribution[].countnumber

repliednumber

unrepliednumber

response_ratenumber | null

median_response_hoursnumber | null

rating_onlynumber

overduenumber

oldest_overdue_hoursnumber | null

awaiting_auto_replynumber

sla_compliance_ratenumber | null

Examples

Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.

curl
export SERVICEVIN_API_KEY=svk_live_…

curl -X POST https://www.servicevin.com/api/v1/reviews/insights \
  -H "Authorization: Bearer $SERVICEVIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

TypeScript (fetch)
const res = await fetch("https://www.servicevin.com/api/v1/reviews/insights", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
});

// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;

Python (requests)
import os, requests

res = requests.post(
    "https://www.servicevin.com/api/v1/reviews/insights",
    headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
    json={},
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(payload["error"]["code"])
data = payload["data"]

MCP tools/call — https://www.servicevin.com/api/mcp
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "reviews.insights",
    "arguments": {}
  }
}

Refusals

The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.

StatusCodeWhen
404not_foundThe id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both.
403forbiddenThis login does not hold marketing.access.
422validation_errorAn argument was wrong. The message names the field.
429rate_limitedToo many read calls. Back off and retry.
500internal_errorSomething failed on our side. Nothing was changed.