GuideGuides/Review-response workflows
How do I build an automated review-response workflow with the API?

How to Build Automated Review-Response Workflows

Build a review-response workflow that routes reviews by rating and sentiment, drafts replies, approves them, and pushes them back to the platform.

At a glance

What this guide accomplishes
Turn incoming reviews into a routed, approvable reply workflow that posts back to the source platform.
APIs used
Reference
Reviews
Prerequisites
  • An API key with write access
  • Locations whose reviews are already syncing
Typical use cases
  • Auto-replying to five-star reviews and routing the rest
  • Building an approval queue for owner replies
  • Keeping response rate high across many locations

Automating review responses is mostly a routing problem. Every reply is public and posts under the business's name, so the automation you actually want is not "reply to everything". It is "handle the easy ones, and get the rest in front of a person fast". Fetch, classify, draft, approve, send: that is the loop, and the risk lives entirely in who gets to skip the approve step.

The loop

  1. FetchGET /api/v1/reviews?status=unrepliedPull what is waiting, newest first
  2. ClassifyBy rating and sentiment: auto-handle, or route to a person
  3. DraftA template for the simple case, a note for the escalation
  4. ApproveA human okays anything negative or sensitive
  5. SendPOST /api/v1/reviews/replyPushed to the source platform
A review, routed to the right handler

Fetch what is waiting

GET /api/v1/reviews with status=unreplied is the queue. Pull it newest first and let the filters do the first cut: ratings and sentiment are exactly the signals your routing keys on. Each review carries rating, sentiment, a topics array of themes, and respondable, which is false when the source will not accept a reply.

curl "https://ai.synup.com/api/v1/reviews?locationId=LOCATION_ID&clientId=CLIENT_ID&status=unreplied&sort=newest&limit=100" \
  -H "Authorization: Bearer $SYNUP_API_KEY"

Route before you reply

The rule that keeps a workflow safe is simple: the higher the risk, the more human it gets. A reasonable default:

ReviewHandling
5 star, positive, no text or short thanksAuto-reply from a rotated template
4 star, positiveAuto-reply, or one-click approve
3 star, or mixed sentimentDraft, route to a person
1 to 2 star, or negative sentimentDraft, require approval, notify the location
respondable: falseDo not queue a reply at all

The point is not these exact thresholds. It is that a rating and a sentiment are enough to split "send now" from "a person needs to see this", and the API hands you both on every review.

Draft, approve, then send

POST /api/v1/reviews/reply is the send. It posts the reply under the business's name and pushes it to the platform, returning whether the push happened now, is queued, or failed with a retryable flag.

curl -X POST https://ai.synup.com/api/v1/reviews/reply \
  -H "Authorization: Bearer $SYNUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reviewId": "REVIEW_ID", "text": "Thanks so much for the kind words. We will pass this on to the team." }'
const API = "https://ai.synup.com/api/v1";
const headers = {
  Authorization: `Bearer ${process.env.SYNUP_API_KEY}`,
  "Content-Type": "application/json",
};

const templates = {
  five: ["Thank you for the kind words.", "We really appreciate you taking the time."],
};

function route(review) {
  if (!review.respondable) return { action: "skip" };
  if (review.rating >= 5 && review.sentiment === "positive") return { action: "auto" };
  if (review.rating <= 2 || review.sentiment === "negative") return { action: "approve" };
  return { action: "draft" };
}

async function handle(review) {
  const decision = route(review);
  if (decision.action === "auto") {
    const text = templates.five[Math.floor(Math.random() * templates.five.length)];
    return reply(review.id, text);
  }
  // draft or approve: park it for a person instead of sending
  return addNote(review.id, `Routed to ${decision.action}: ${review.rating} star, ${review.sentiment}`);
}

async function reply(reviewId, text) {
  const res = await fetch(`${API}/reviews/reply`, { method: "POST", headers, body: JSON.stringify({ reviewId, text }) });
  if (res.status === 422) return { status: "not_respondable" };
  if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
  return (await res.json()).data;
}

async function addNote(reviewId, body) {
  const res = await fetch(`${API}/reviews/notes`, { method: "POST", headers, body: JSON.stringify({ reviewId, body }) });
  if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
  return (await res.json()).data;
}
import os, random, requests

API = "https://ai.synup.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SYNUP_API_KEY']}", "Content-Type": "application/json"}
TEMPLATES = {"five": ["Thank you for the kind words.", "We really appreciate you taking the time."]}

def route(review):
    if not review.get("respondable"):
        return "skip"
    if review["rating"] >= 5 and review["sentiment"] == "positive":
        return "auto"
    if review["rating"] <= 2 or review["sentiment"] == "negative":
        return "approve"
    return "draft"

def handle(review):
    action = route(review)
    if action == "auto":
        return reply(review["id"], random.choice(TEMPLATES["five"]))
    return add_note(review["id"], f"Routed to {action}: {review['rating']} star, {review['sentiment']}")

def reply(review_id, text):
    r = requests.post(f"{API}/reviews/reply", headers=HEADERS, timeout=30,
                      json={"reviewId": review_id, "text": text})
    if r.status_code == 422:
        return {"status": "not_respondable"}
    r.raise_for_status()
    return r.json()["data"]

def add_note(review_id, body):
    r = requests.post(f"{API}/reviews/notes", headers=HEADERS, timeout=30,
                      json={"reviewId": review_id, "body": body})
    r.raise_for_status()
    return r.json()["data"]

The escalations that a person needs to see go into a note, not a reply. POST /api/v1/reviews/notes attaches team-only text (GET /api/v1/reviews/notes reads it back) that never reaches the platform, which is where the "who is handling this" conversation belongs.

Measure whether it is working

GET /api/v1/reviews/analytics gives the number that tells you the workflow is doing its job: response rate, with a period-over-period trend, per location. Watch it climb as the automation takes the easy volume off your team. If response rate is flat, the routing is sending too much to people.

Next

When this grows into a product with campaigns, screening and widgets, that is the reputation platform guide. To let a model write the drafts instead of templates, see build an AI agent that responds to reviews. The single reply call is the respond to a review recipe.