Navigation

Evaluate an action

Evaluate a custom action from your backend and get the full evaluation object back in the same response. This is the server-to-server counterpart of the browser SDK's evaluate: the same policies run, the verdict is returned directly, and there is no second call to confirm it.

Pass a content block to moderate something a user wrote. Rupt reads the text, adds the content checks (content_flagged, content_category, content_severity, content_confidence, content_status) to the policy conditions, and returns the categories it found alongside the verdict. A review verdict places the evaluation in the dashboard's review queue; the decision arrives on the review.decided webhook.

The reserved actions login, signup and access stay on the browser SDK and do not accept content.

Path parameters


action string

Your custom action name, for example message, listing or job_post. Lowercase letters, digits, hyphens and underscores, 2 to 64 characters. Policies whose trigger includes this action run.

Body parameters


user string

The user's external ID. Required when content is present, and the identifier every account check hangs off.


ip string (optional)

The end user's IP address. Send it whenever you have it. Without it the network checks report unknown, because your server's own address says nothing about the user.


email string (optional)

The user's email address, for the email quality checks.


phone string (optional)

The user's phone number.


content object (optional)

The content to moderate.

content.type string

Only text today. html, url and image are reserved and return a validation error.


content.text string

The text to judge, up to 20,000 characters. The first 4,000 are stored with the evaluation so a reviewer can read them.


content.id string (optional)

Your own id for this message or post, up to 200 characters. It comes back on every webhook and lets you act on a decision without a lookup.


content.conversation string (optional)

One id for the thread, channel or group the content belongs to. When set and context is absent, Rupt reads the last ten messages it already evaluated in that conversation and gives them to the model as context.


content.context array (optional)

Up to ten earlier turns of the conversation, oldest first, 4,000 characters in total. Each turn has user (the sender's external ID), id (your id for that message) and text. Overrides the automatic look-back. Send it on the first call for a conversation that existed before you integrated, then let the look-back take over.


metadata object (optional)

Custom metadata stored on the evaluation and available to metadata policy conditions.

How the content check runs


The call is synchronous. Rupt classifies the text inside a fixed time budget of a few seconds, runs your policies with the result, stores the evaluation and responds. Nothing is queued, so the verdict in the response is final unless it is review, in which case a person decides later and the decision arrives on the webhook.

When the classifier does not answer inside the budget, content.status is unavailable, content.categories is empty, the content checks report no value, and the policies run on the account checks alone. A policy on content_status equals unavailable decides whether those messages are held or let through; without one they are treated like any message that matched no policy.

The same text with the same context is cached for 24 hours, so a retried request returns the same categories without a second classification. The cache is keyed on the action, the text and the context; the user and metadata still go through the policies on every call.

Two categories are always raised at maximum severity whatever the model's own estimate: child_safety and malicious_code. Every other category carries the severity and confidence the model assigned, and a category is dropped when its confidence is under 0.6.

Context and look-back


The model judges only the message in content.text; earlier turns are context that can raise or lower the severity, never the subject. Each turn is labelled by whether it came from the author or from someone else, so a victim's reply to a scam is judged as the victim's own words.

With content.conversation set and no content.context, Rupt loads the last ten messages it already evaluated in that conversation, newest first, and passes them oldest first. Only the message bodies Rupt stored are used, which is the first 4,000 characters of each. With content.context set, the look-back is skipped and your turns are used as given. With neither, the message is judged on its own.

What is stored


Every evaluation keeps the first 4,000 characters of content.text, the content and conversation ids, the categories with their evidence quotes, the summary and the language, so a reviewer sees the message with the evidence highlighted. The ids of the context turns are always kept; their text is kept only when the message was flagged or the verdict was anything other than allow, which is when a reviewer needs it.

The text is never returned by the API. The evaluation object carries the verdict, categories and evidence, not the message, so an evaluation id cannot be used to read what a user wrote.

Verdicts


allow and deny mean deliver and drop. review means hold the content and wait for the review.decided webhook, which carries the decision with your content and conversation ids. challenge comes with a redirect for the author to complete before the content goes through. suspend blocks the content and keeps the user out until you lift the suspension. add_to_list and remove_from_list apply the list change and never block, so deliver the content. See verdicts.

Returns


Returns an evaluation object with content set when a content block was sent, review set with status pending when the verdict is review, and checks carrying the five content checks alongside the account checks. The reasons array names the conditions of the winning policy.

Errors


A 400 with an errors array when the body is invalid, including: content on login, signup or access; a content.type other than text; content.text empty or over 20,000 characters; content.id over 200 characters; more than ten content.context turns or more than 4,000 characters of context; a turn missing user, id or text; user missing when content is present; an action name that is not 2 to 64 lowercase letters, digits, hyphens or underscores. A 401 when the secret is missing or wrong.

POST/v3/evaluate/:action
const evaluation = await rupt.evaluate({
  action: "message",
  user: "u_8fa2",
  ip: "203.0.113.9",
  content: {
    type: "text",
    id: "msg_91f",
    conversation: "conv_77",
    text: "Deposit first, then we move to WhatsApp",
    context: [
      { user: "u_1c9", id: "msg_90a", text: "Is the unit still available?" },
      { user: "u_8fa2", id: "msg_90b", text: "Yes, but I am abroad this month" },
    ],
  },
  metadata: { listing_id: "L-1042" },
});
Response
{
  "id": "68c1f3a2c4d5e6f7a8b9c0d1",
  "action": "message",
  "verdict": "review",
  "reasons": ["content_category", "new_user"],
  "user": {
    "rupt_id": "68c1f3a2c4d5e6f7a8b9c0d0",
    "id": "u_8fa2",
    "suspended": false
  },
  "policy": {
    "id": "68c1f3a2c4d5e6f7a8b9c0c9",
    "name": "Review scam signals from new accounts",
    "action": { "type": "review" }
  },
  "review": { "status": "pending" },
  "content": {
    "type": "text",
    "id": "msg_91f",
    "conversation": "conv_77",
    "status": "reviewed",
    "flagged": true,
    "severity": "high",
    "confidence": 0.94,
    "categories": [
      {
        "name": "off_platform",
        "severity": "high",
        "confidence": 0.94,
        "evidence": "then we move to WhatsApp"
      },
      {
        "name": "scam",
        "severity": "medium",
        "confidence": 0.71,
        "evidence": "Deposit first"
      }
    ],
    "summary": "Asks for a deposit before anything is verified and moves the conversation to WhatsApp.",
    "language": "en"
  },
  "checks": {
    "is_new_user": true,
    "content_flagged": true,
    "content_category": ["off_platform", "scam"],
    "content_severity": "high",
    "content_confidence": 0.94,
    "content_status": "reviewed"
  },
  "risks": [],
  "metadata": { "listing_id": "L-1042" },
  "consumed": false,
  "createdAt": "2026-09-10T14:02:09.000Z",
  "updatedAt": "2026-09-10T14:02:09.000Z"
}