ProductsDocsGuidesPricing
Sign inGet Started

Intelligence APIs for developers.

Product

  • All APIs
  • Support Triage
  • Pricing

Developers

  • Getting started
  • Guides
  • API reference
  • Errors
  • Dashboard

Company

  • About
  • Security
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
© Kovca. kovca.io
GuidesBusiness Validation

Moderating Product Reviews With Your Own Rules

Moderate marketplace reviews against rules you write: block spam and off-platform contact details, keep honest criticism, and hold the gray areas for review. Code in curl, TypeScript and Python.

Content Intelligence7 min readcURL · TypeScript · Python
allowreviewblock

A marketplace's reviews section faces pressure from two directions. One is spam: "Great blender, but you can get it 40% cheaper at bestdeals-example.shop, message me on WhatsApp." That review is an ad for a scam, and every hour it stays up costs trust. The other is over-moderation: a one-star review that says the lid feels flimsy is exactly what buyers need to see. A filter that removes it makes every other review look paid for.

Generic moderation tools don't know where your line is. This guide builds a moderation step around rules you write. Spam and off-platform contact are blocked, honest criticism is published, and the gray areas are held for a person. The evaluation step uses the Kovca Content Intelligence API.

Write the policy before the code

Moderation quality depends far more on the policy than on the classifier. Kovca takes three lists:

  • policy.disallowed: concrete things that aren't allowed, up to 20, in plain language. "Links or contact details that take buyers off the platform" is a good rule. "Inappropriate content" isn't, because nobody can check it consistently.
  • policy.allowed: things that are explicitly fine, up to 20, and considered for every check. "Negative reviews and criticism of products or service" protects your one-star reviews.
  • policy.block_categories: which of the default categories (spam, harassment, threats, sexual_content, violent_content, prohibited_promotion) should block outright when likely. Anything you don't list is held for review instead.

Default categories alone never block. If you send no rules and no block_categories, the worst result you'll get is review. Blocking is always a decision you opted into. That makes the API safe to try with an empty policy, and a policy is what makes it useful.

The pipeline

  1. 1
    Review submitted
    text, product, reviewer account
  2. 2
    Your checks
    rate limits, verified purchase, duplicate text
  3. 3
    Kovca Content Intelligence
    decision, categories, policy_matches
    Moderation queuedecision: review
  4. 4
    Publish or block
    allow → live, block → tell the author why
  5. 5
    Audit log
    request_id, matched rule, moderator outcome

1Handle the cheap cases first

Some checks don't need a model. Rate-limit new accounts, reject exact-duplicate text, and require a verified purchase if your marketplace does. These are your rules, and they're cheaper and easier to explain than any classifier.

2Evaluate the review

Send the text as content (up to 5,000 characters), with content_type and context so the evaluation knows it's a product review and not a chat message. The content is treated purely as data, so instructions written inside a review are ignored.

curl

cURL
curl https://kovca.io/api/v1/content/moderate \
  -H "Authorization: Bearer $KOVCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Great blender but you can get it 40% cheaper at bestdeals-example.shop, just message me on WhatsApp +1 555 0100.",
    "content_type": "product_review",
    "context": "Marketplace product reviews",
    "policy": {
      "disallowed": ["Links or contact details that take buyers off the platform", "Reviews of a different product than the one listed"],
      "allowed": ["Negative reviews and criticism of products or service"],
      "block_categories": ["spam", "threats", "harassment"]
    }
  }'
JSON
{
  "request_id": "req_Lt6qN3wRv8KzM1pY",
  "decision": "block",
  "recommended_action": "block_submission",
  "categories": [
    { "category": "spam", "assessment": "likely" },
    { "category": "policy_violation", "assessment": "likely" }
  ],
  "policy_matches": [
    { "rule": "Links or contact details that take buyers off the platform", "source": "policy.disallowed[0]", "assessment": "likely" }
  ],
  "signals": ["custom_rule_matched"]
}

policy_matches quotes your rule exactly. That's the sentence to show the author ("Your review was not published because it includes contact details that take buyers off the platform") and the sentence a moderator reviews on appeal.

TypeScript (Node 18+)

TypeScript
type Assessment = "likely" | "possible";
type Moderation = {
  request_id: string;
  decision: "allow" | "review" | "block" | "insufficient_information";
  recommended_action: "publish" | "hold_for_review" | "block_submission" | "request_information";
  categories: { category: string; assessment: Assessment }[];
  policy_matches: { rule: string; source: string; assessment: Assessment }[];
  signals: ("default_criteria_only" | "custom_rule_matched" | "ambiguous" | "too_short_to_evaluate")[];
};

const policy = {
  disallowed: ["Links or contact details that take buyers off the platform", "Reviews of a different product than the one listed"],
  allowed: ["Negative reviews and criticism of products or service"],
  block_categories: ["spam", "threats", "harassment"],
};

export async function moderateReview(text: string): Promise<Moderation | null> {
  const res = await fetch("https://kovca.io/api/v1/content/moderate", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.KOVCA_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ content: text.slice(0, 5000), content_type: "product_review", context: "Marketplace product reviews", policy }),
  });
  return res.ok ? res.json() : null; // null → hold for review, never auto-publish
}

Python

Python
import os, requests

POLICY = {
    "disallowed": ["Links or contact details that take buyers off the platform",
                   "Reviews of a different product than the one listed"],
    "allowed": ["Negative reviews and criticism of products or service"],
    "block_categories": ["spam", "threats", "harassment"],
}

def moderate_review(text):
    r = requests.post(
        "https://kovca.io/api/v1/content/moderate",
        headers={"Authorization": f"Bearer {os.environ['KOVCA_API_KEY']}"},
        json={"content": text[:5000], "content_type": "product_review",
              "context": "Marketplace product reviews", "policy": POLICY},
        timeout=30,
    )
    return r.json() if r.ok else None

Note the failure mode. Triage fails open (an unclassified ticket still reaches a queue), but moderation should fail closed into review. If the call fails, hold the review instead of publishing it unchecked.

3Act on the decision

The decision is the first rule that matches, in this order:

decisionrecommended_actionWhenWhat you do
insufficient_informationrequest_informationToo short to evaluate, like "ok" or "👍"Publish the star rating only, or ask for more detail.
blockblock_submissionA likely match on a concrete disallowed rule, or a likely category in block_categoriesReject and show the author the matched rule.
reviewhold_for_reviewAny other match, including possible onesQueue for a moderator. Don't publish yet.
allowpublishNothing matchedPublish.

With the policy above, "Arrived two days late and the lid is a bit flimsy, but it blends smoothies well. Three stars." matches nothing disallowed and is covered by the allowed rule, so it's published. A personal attack on a named seller is blocked when harassment is assessed as likely, because harassment is in block_categories. If it's only possible, the review is held for a moderator.

Vague rules can't block. A subjective rule such as "off-topic" can send content to review but never blocks it on its own. If you want something blocked automatically, write it as a rule someone could check by reading the text.

4Close the loop with moderators

Log the request_id, the decision, the matched rules, and what the moderator finally did. Two patterns are worth watching for:

  • A rule that's almost always overturned. It's probably worded too broadly. Narrow it, or move it from disallowed to a note for moderators.
  • A category that's always upheld when held for review. That's a candidate for block_categories.

Kovca doesn't store content, so this log is your only history. Keep it.

Rolling it out

  1. Shadow mode. Evaluate every new review but publish as you do today. Have moderators spot-check block decisions and a sample of allow decisions.
  2. Block the obvious. Turn on automatic blocking for your concrete disallowed rules and spam. Put everything marked review in the queue.
  3. Expand carefully. Add categories to block_categories one at a time, based on the overturn data from step 4.

Volume and cost

One request per submitted review, or per edit. The Free plan covers 1,000 successful requests a month at 10 per minute. Pay As You Go is $0.005 per successful request at 60 per minute, with a spending cap (pricing). Content is never stored by Kovca, and the API never publishes, deletes, or suspends anything itself.

Try it with your hardest reviews

Collect a dozen reviews your moderators disagreed about, write your policy as three or four concrete rules, and run them through. Sign up free, create a key, and test in the playground. Category definitions and decision rules are in the Content Intelligence docs.

On this page

  • Write the policy before the code
  • The pipeline
  • Rolling it out
  • Volume and cost
  • Try it with your hardest reviews

Content Intelligence

Evaluate user-generated content against your platform rules and get a structured moderation recommendation.

Try it in the playground

Keep going

Content IntelligenceAPI reference
billingtechnicalneeds_review
Support Triage7 min read

Automating Support Ticket Triage in Your Helpdesk Pipeline

Build a support ticket triage pipeline: webhook intake, category and priority classification, human-review routing, and safe fallbacks. Code in curl, TypeScript and Python.

Read guide
monitorcontact_carrierinvestigate
Shipment Intelligence6 min read

Catching Delivery Exceptions Before Your Customers Do

Turn raw carrier tracking events into routed delivery exceptions: late and stalled parcels, address problems, losses and disputes. A scheduled pipeline with curl, TypeScript and Python.

Read guide
same_variantdifferent_variantuncertain
Catalog Matching9 min read

How to Match Supplier Product Catalogs to Your Own SKUs

A practical guide to matching supplier feeds to your SKUs: blocking candidates, using GTINs safely, handling variants, and routing uncertain matches. Code in curl, TypeScript and Python.

Read guide