Documentation
Reviews
reviews.private_feedback
REST
ShippingPOST /api/v1/reviews/private_feedbackMCP tool
Livereviews.private_feedbackExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Reviews domain.
Operating contract
What customers told the SHOP directly after a job — a star rating and, often, a note. This never appears in public, anywhere.
DO NOT CONFUSE THIS WITH A GOOGLE REVIEW, and never reply to one publicly. A customer who rated the shop three stars privately said that in confidence; a public reply quoting it has broadcast something they chose not to publish, and no apology fixes that.
routed_to says where the rating sent them next. A low rating is deliberately routed to the shop rather than to Google — that is the whole point of the mechanism, and it is why the private ratings skew lower than the public ones.
note is the customer's own words. It is the most useful text in this whole domain and nothing else surfaces it.
job_id and customer_id, where set, are what jobs.get and customers.get take — reading the work behind a bad rating is usually the answer to it.
TRUNCATION: total is the exact number in the window and omitted is how many are not here. When omitted is not 0, call again with offset set to next_offset.
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.
Partial answers
omitted is how many matching records are not in the response, and 0 is a real answer meaning you have all of them. Continue with next_offset.
Do not summarise from a truncated response
Input
| Field | Type | Description |
|---|---|---|
| max_rating | integer1–5 | Only ratings at this level or below. 3 is the unhappy end. |
| days | integer1–730 | How many days back to read, 1-730. Defaults to 90. Default:90 |
| limit | integer1–100 | How many ratings to return, 1-100. Defaults to 25. Default:25 |
| offset | integermin 0 | How many to skip. Pass the previous response's next_offset to continue. Default:0 |
Output
| Field | Type | Description |
|---|---|---|
| items | object[] | — |
| items[].id | string | — |
| items[].rating | number | — |
| items[].note | string | null | — |
| items[].routed_to | string | — |
| items[].source | string | — |
| items[].customer_id | string | null | — |
| items[].job_id | string | null | — |
| items[].rated_at | string | — |
| total | number | — |
| omitted | number | — |
| next_offset | number | 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.
export SERVICEVIN_API_KEY=svk_live_…
curl -X POST https://www.servicevin.com/api/v1/reviews/private_feedback \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"days":90,"limit":25,"offset":0}'const res = await fetch("https://www.servicevin.com/api/v1/reviews/private_feedback", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"days": 90,
"limit": 25,
"offset": 0
}),
});
// 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;import os, requests
res = requests.post(
"https://www.servicevin.com/api/v1/reviews/private_feedback",
headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
json={
"days": 90,
"limit": 25,
"offset": 0
},
timeout=30,
)
payload = res.json()
if not res.ok:
raise RuntimeError(payload["error"]["code"])
data = payload["data"]{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "reviews.private_feedback",
"arguments": {
"days": 90,
"limit": 25,
"offset": 0
}
}
}Refusals
The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.
| Status | Code | When |
|---|---|---|
| 404 | not_found | The id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both. |
| 403 | forbidden | This login does not hold marketing.access. |
| 422 | validation_error | An argument was wrong. The message names the field. |
| 429 | rate_limited | Too many read calls. Back off and retry. |
| 500 | internal_error | Something failed on our side. Nothing was changed. |