ThemysThemys/Docs

POST /api/analyze

Analyze Terms of Service text with AI to get flagged clauses, attention guidance, and full extraction.

Endpoint

code
POST /api/analyze

Analyzes Terms of Service text and returns structured review guidance including a summary, flagged clauses, full extraction breakdown, and a backward-compatible riskScore ranking signal.

Authentication

Include your Clerk session token in the Authorization header:

code
Authorization: Bearer <your_session_token>

Request Body

json
{
  "text": "Terms of Service\n\n1. Acceptance of Terms\nBy accessing...",
  "sourceUrl": "https://example.com/terms-of-service",
  "domain": "example.com"
}

FieldTypeRequiredDescription
textstringOne of text or urlThe Terms of Service text to analyze
urlstringOne of text or urlURL to extract text from (uses /api/extract internally)
sourceUrlstringNoOriginal public URL for already-extracted text; used as metadata and never fetched
domainstringNoDomain name (e.g., "example.com"). Used for display and grouping in scan history.

Provide exactly one analysis input: text or url. When you already extracted text from a URL, send the text with sourceUrl so the origin is retained without fetching it again.

Response

json
{
  "riskScore": 7,
  "riskLevel": "red",
  "summary": "This Terms of Service grants the company broad rights to user data and content. It includes mandatory arbitration with a class action waiver, unilateral right to modify terms, and a liability cap limited to the amount paid in the last 12 months.",
  "flags": [
    {
      "category": "Data Privacy",
      "severity": "high",
      "text": "We may share your personal information with third-party partners for marketing and advertising purposes.",
      "explanation": "Your personal data can be shared with third parties for advertising without additional consent.",
      "riskScore": 8
    },
    {
      "category": "Arbitration",
      "severity": "high",
      "text": "You agree to resolve any disputes through binding arbitration and waive your right to participate in a class action lawsuit.",
      "explanation": "You give up the right to sue in court or join a class action. All disputes go through private arbitration.",
      "riskScore": 8
    },
    {
      "category": "Billing",
      "severity": "medium",
      "text": "We reserve the right to modify pricing at any time with 30 days notice.",
      "explanation": "The company can raise your subscription price with just 30 days notice.",
      "riskScore": 5
    }
  ],
  "fullExtraction": [
    {
      "title": "Data Privacy",
      "keyPoints": [
        {
          "title": "Third-party data sharing",
          "detail": "Collects name, email, IP address, device info, and usage data. Shares data with third-party advertisers.",
          "severity": "warning"
        },
        {
          "title": "Data deletion",
          "detail": "Users can request data deletion via email.",
          "severity": "info"
        }
      ]
    }
  ]
}

Response Fields

Top Level

FieldTypeDescription
riskScorenumberBackward-compatible internal ranking signal from 1-10; do not treat as legal advice or an authoritative legal risk determination
riskLevelstring"green", "yellow", or "red"
summarystringAI-generated overview of the document
flagsArrayList of flagged clauses with details
fullExtractionArray \nullStructured breakdown by section
usageobject \nullRate limit and credit info (only for authenticated users)

Flags Array

Each item in flags represents a clause that raised a concern. riskScore is retained for API compatibility and internal ranking only:

FieldTypeDescription
categorystringTopic area (Data Privacy, Arbitration, Liability, Content & IP, Billing, Account)
severity"low" \"medium" \"high"How significant the concern is
textstringExact text from the original document
explanationstringPlain-English description of what the clause means
riskScorenumberBackward-compatible internal ranking signal for this clause (1-10)

Full Extraction Array

Each item in fullExtraction represents a section of the document:

FieldTypeDescription
titlestringSection name
keyPointsArrayKey points, each with title, detail, quote, and severity

LLM Pipeline

The analysis uses a multi-model pipeline with automatic fallback:

PriorityModelProvider
1google/gemini-2.5-flash-liteOpenRouter
2xiaomi/mimo-v2.5OpenRouter
3google/gemma-4-31b-it:freeOpenRouter
4minimax/minimax-m3OpenRouter
5gemini-2.5-flash-liteGoogle Generative AI (direct)
6Hardcoded fallbackReturns a neutral score with a "review manually" message

If all models fail, the API returns neutral review guidance with a single "Unable to analyze" flag and a backward-compatible neutral riskScore.

Code Examples

JavaScript (fetch)

javascript
// First, extract the text
const extractRes = await fetch("https://themys.ca/api/extract", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_SESSION_TOKEN",
  },
  body: JSON.stringify({
    url: "https://example.com/terms-of-service",
  }),
});

const { text } = await extractRes.json();

// Then, analyze it
const analyzeRes = await fetch("https://themys.ca/api/analyze", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_SESSION_TOKEN",
  },
  body: JSON.stringify({
    text,
    sourceUrl: "https://example.com/terms-of-service",
    domain: "example.com",
  }),
});

const result = await analyzeRes.json();

console.log(`Internal ranking signal: ${result.riskScore}`);
console.log(`Summary: ${result.summary}`);
console.log(`Flags: ${result.flags.length} clauses flagged`);

cURL

bash
# Step 1: Extract text
EXTRACT=$(curl -s -X POST https://themys.ca/api/extract \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -d '{"url": "https://example.com/terms-of-service"}')

TEXT=$(echo $EXTRACT | jq -r '.text')

# Step 2: Analyze text
curl -X POST https://themys.ca/api/analyze \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -d "{\"text\": \"$TEXT\", \"sourceUrl\": \"https://example.com/terms-of-service\", \"domain\": \"example.com\"}"

Error Responses

Missing Input (400)

json
{
  "error": "Provide a URL or pasted terms text"
}

Analysis Failed (500)

json
{
  "error": "Internal server error"
}

This is rare. Retry after a short wait. If it persists, the LLM providers may be experiencing outages.

Rate Limited (402)

json
{
  "error": "Daily scan limit reached",
  "upgradeRequired": true,
  "tier": "free",
  "scansRemaining": 0,
  "scansLimit": 5
}

You've exceeded your plan's daily scan limit. Purchase credits or upgrade to continue.

Rate Limits

Each analysis request consumes one scan from your daily limit, or one credit if you've exceeded it.

PlanDaily Scans
Free5
Base25
Advanced100

Was this page helpful?