How to Auto-Moderate User-Generated Product Reviews with AI Classification

Catch spam, profanity, and personal data in incoming WooCommerce product reviews automatically, publish the clean ones, and send only the borderline cases to a person for a quick approval.

What This Integration Does

User-generated reviews are valuable social proof, but they also arrive full of spam links, abusive language, and personal information that you do not want published on a product page. Manually reading every submission does not scale, and a blunt keyword filter either blocks legitimate reviews or lets bad ones through. This Spojit workflow puts an Agent-mode Connector node in the middle: each new review is classified into a clean structured verdict, obviously good reviews are approved and published to WooCommerce with no human in the loop, and only the genuinely uncertain ones interrupt a person.

The workflow starts from a Webhook trigger that receives each new review as an HTTP POST. The review text flows into a Connector node running in Agent mode, which returns a fixed JSON verdict (a recommended action plus reasons and a confidence score). A Condition node routes on that verdict: approve goes straight to publishing, reject is dropped with a Slack notice, and review pauses at a Human approval step before publishing. Each POST runs independently, leaves a moderated review in your store (or a logged rejection), and re-running the same review simply re-evaluates it, so the workflow is safe to retry.

Prerequisites

  • A WooCommerce connection with API keys that have read and write access to products and reviews. See the steps in Adding a New Connection.
  • A Slack connection authorized to post to the channel you use for moderation alerts (for example #review-moderation).
  • A signing connection for the Webhook trigger. Use the Custom scheme if you control the system that posts reviews, or Spojit if you sign requests yourself. See Setting Up a Webhook Connection.
  • At least one approver (a User, Role, or Team) available in your workspace for the Human node, plus the moderation team agreed on what "borderline" means.
  • A source that POSTs each new review to a URL: your store's review hook, a forwarding rule, or a small script that calls the workflow when a review is submitted.

Step 1: Receive the review with a Webhook trigger

Add a Trigger node and set its type to Webhook. Choose your signing connection so Spojit can verify each request, then copy the generated URL and point your review source at it. The trigger parses the JSON body and exposes it as {{ input }}. Configure the posting side to send a payload like this:

{
  "reviewId": 4821,
  "productId": 159,
  "reviewer": "Sam P.",
  "reviewerEmail": "sam@example.com",
  "rating": 5,
  "review": "Great mug! Call me on 555-0142 to chat. Visit cheap-pills-now.example"
}

The Webhook trigger returns 202 with an executionId immediately, so your review source is not held open while moderation runs. If your source can send a unique event id header, turn on dedup so the same review submitted twice does not run twice.

Step 2: Classify the review with a Connector node in Agent mode

Add a Connector node, pick your AI-capable connector, and switch it to Agent mode. Agent mode lets the agent reason over the free text and, crucially, lets you attach a Response Schema so the output is always valid JSON you can branch on. Write a prompt that hands it the review and asks for a single moderation verdict:

You moderate user-submitted product reviews. Classify this review:

Rating: {{ input.rating }}
Review: {{ input.review }}

Decide one action: "approve" (clean and useful),
"reject" (spam, profanity, or off-topic), or
"review" (borderline, needs a human).
Flag any of: spam, profanity, pii (emails, phone numbers,
addresses). Return your confidence from 0 to 1.

Set the Response Schema so the node always returns the same shape:

{
  "type": "object",
  "properties": {
    "action": { "type": "string", "enum": ["approve", "reject", "review"] },
    "flags": {
      "type": "array",
      "items": { "type": "string", "enum": ["spam", "profanity", "pii"] }
    },
    "confidence": { "type": "number" },
    "reason": { "type": "string" }
  },
  "required": ["action", "flags", "confidence", "reason"]
}

Name the output variable verdict. Downstream nodes then read {{ verdict.action }}, {{ verdict.flags }}, {{ verdict.confidence }}, and {{ verdict.reason }}.

Step 3: Route on the verdict with a Condition node

Add a Condition node to split the three paths. Because the structured output guarantees action is one of three known values, your branching stays clean. Create a first condition that checks whether {{ verdict.action }} equals approve: its true branch goes to publishing (Step 4). For the remaining cases, chain a second Condition that checks whether {{ verdict.action }} equals review: the true branch goes to the Human node (Step 5), and the false branch is treated as a reject (Step 6). You can also tighten the rules here, for example only auto-approving when {{ verdict.confidence }} is above 0.8 and routing the rest to a human even if the action was approve.

Step 4: Publish clean reviews to WooCommerce

On the approve branch, add a Connector node for WooCommerce in Direct mode. WooCommerce product reviews are managed through the store's reviews endpoint, which you call with the raw-api-request tool. Set the method to POST, the path to products/reviews, and the body to the review fields plus an approved status:

{
  "method": "POST",
  "path": "products/reviews",
  "body": {
    "product_id": {{ input.productId }},
    "review": "{{ input.review }}",
    "reviewer": "{{ input.reviewer }}",
    "reviewer_email": "{{ input.reviewerEmail }}",
    "rating": {{ input.rating }},
    "status": "approved"
  }
}

Direct mode is the right choice here because the action is a single, predictable call with no AI cost. If a review already exists in WooCommerce and you only want to flip its status, use raw-api-request with a PUT to products/reviews/{{ input.reviewId }} and a body of { "status": "approved" } instead.

Step 5: Escalate borderline reviews with a Human node

On the review branch, add a Human node so a person decides. Set the Message to surface the model's reasoning so the approver has context, for example: Review {{ input.reviewId }} flagged {{ verdict.flags }} ({{ verdict.confidence }}). Reason: {{ verdict.reason }}. Text: {{ input.review }}. Set Urgency to Normal, add a Timeout (minutes) such as 1440 (one day) so stale items do not block forever, and turn on Email approvers if your moderators do not watch the in-app inbox. Add an Approval slot with your moderation User, Role, or Team as the atom; the workflow continues only when the slot is satisfied.

When the approver clicks Approve in the Approvals inbox at /approvals, the node outputs { approved: true, outcome: "APPROVED" } and execution flows into the same WooCommerce publish step from Step 4 (connect the Human node's output into it, or duplicate the publish node on this branch). If the approver rejects or the timeout fires, the run halts at the Human node, so the review is never published. For more on the slot model, see Using Human Approval Nodes.

Step 6: Notify the team about rejections in Slack

On the reject branch, add a Connector node for Slack in Direct mode and pick the send-message tool. Set the channel to your moderation channel and build a message from the verdict so the team has a record of what was filtered and why:

Rejected review {{ input.reviewId }} on product {{ input.productId }}
Flags: {{ verdict.flags }}
Reason: {{ verdict.reason }}
Text: {{ input.review }}

This branch deliberately does not write to WooCommerce, so rejected content never reaches a product page. You can optionally add a second Connector node here using WooCommerce raw-api-request with a PUT to products/reviews/{{ input.reviewId }} and body { "status": "spam" } if the review already exists in the store and you want it explicitly marked.

Tips

  • Use {{ verdict.confidence }} as a tuning dial: start by routing anything under 0.85 to the Human node, then lower the threshold as you trust the classification more, so fewer reviews need a person over time.
  • Keep the prompt focused on the three actions and the three flags. The tighter the Response Schema enum, the fewer surprises your Condition node has to handle.
  • Ask Miraxa to scaffold this for you with a prompt like "Build a workflow with a Webhook trigger, a Connector node in Agent mode that returns a JSON verdict, a Condition node on the verdict action, and a Human node for borderline cases," then fine-tune each node in the properties panel.
  • For high review volume, batch is not needed because each POST runs on its own, but watch your WooCommerce API rate limits if a flood of reviews arrives at once.

Common Pitfalls

  • Forgetting the Response Schema on the Agent-mode node. Without it, the output is free text and your Condition node cannot reliably read {{ verdict.action }}.
  • Using WooCommerce API keys without write scope. Read-only keys let classification run but fail silently to publish, so confirm the connection can create and update reviews.
  • Expecting an "on reject do X" path off the Human node. A reject or timeout halts the run; it does not branch. Put any "what to do when a human says no" logic before the Human node or handle it as a separate workflow.
  • Letting personal data leak into Slack. If your reviews routinely contain emails or phone numbers, trim the message in a Transform node before the send-message call so the alert itself does not republish the PII you are trying to catch.

Testing

Before pointing live traffic at the workflow, test each path with a handful of crafted POSTs. Send one obviously clean review, one with a spam link or phone number, and one ambiguous review, then watch the run in the execution history to confirm the Agent node's verdict, the Condition routing, and the final action. Check that the clean one appears in WooCommerce with status: approved, that the spam one triggered a Slack message and was not published, and that the ambiguous one is waiting in the /approvals inbox. Approve and reject it once each to confirm both outcomes behave as expected. Only once all three branches look right should you connect your real review source.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.