Show where AI tutor answers end up in student work

For learning platforms, schools and education teams that offer an AI tutor. The tutor labels every answer it gives. When a submission contains copied tutor text, a check finds the paragraphs and the tutor session, so a teacher can look at the actual conversation instead of relying on a guess.

Endpoints: POST /api/watermark/encode or /tokens, /decode  ·  MCP tools: watermark_encode, watermark_decode  ·  Cost: 1 credit per tutor answer; checks free unless a label is found

The problem

Students use AI tutors, and that is often the point. Teachers still need to see when tutor answers are handed in as a student's own work. AI detectors estimate whether text "looks" machine-written; they produce false accusations, especially against students writing in a second language. A label is different: it is a fact about your tutor's output, added when the answer was generated.

How it works

  1. The tutor labels each answer with an opaque session id, for example ai;tutor;s=s-20931, through POST /api/watermark/encode. Tutors that stream their answers use /tokens, as shown in Label AI-generated text.
  2. Submissions are checked paragraph by paragraph with POST /api/watermark/decode. Paragraphs without a label cost nothing.
  3. A teacher opens the session for each labelled paragraph and sees the question the student asked and the answer the tutor gave.

Use it fairly

  • Tell students that tutor answers carry an invisible label. Transparency is fair, and it changes behaviour on its own.
  • Treat a found label as the start of a conversation, not as a penalty. Quoting the tutor with attribution can be perfectly acceptable.
  • Never treat a missing label as proof that a student worked alone.
  • Keep names out of labels and follow your data-protection rules for minors.

Add it to your workflow

The examples read your API token from TEXTWATERMARKING_API_TOKEN. Each example below is run against the API before this page is published.

Label tutor answers (Python)

If labelling fails, the student still gets the answer; log the failure in your own system.

tutor_label.py

# Label each answer your AI tutor gives (Python 3.10+, pip install requests). 1 credit per answer.
# The label is an opaque session id: anyone with an API token can read it, so never put a student name in it.
import os

import requests

API = "https://textwatermarking.com/api/watermark"
HEADERS = {"Authorization": f"Bearer {os.environ['TEXTWATERMARKING_API_TOKEN']}"}


def label_answer(answer: str, session_id: str) -> str:
    """Return the answer with an invisible 'ai;tutor;s=<session>' label, or unchanged if labelling fails."""
    try:
        r = requests.post(f"{API}/encode", headers=HEADERS, timeout=10,
                          json={"text": answer, "secret": f"ai;tutor;s={session_id}"})
        r.raise_for_status()
        return r.json()["encoded"]
    except requests.RequestException:
        return answer          # never block the student; log the failure in your own system


if __name__ == "__main__":
    print(label_answer("Photosynthesis turns light energy into chemical energy stored in glucose.", "s-20931"))

Check a submission (Python)

Run it when work is handed in, or on demand from the grading view. It also finds tutor text pasted in the middle of a paragraph.

check_submission.py

# Find paragraphs in a submission that carry your tutor's label (Python 3.10+, pip install requests).
# Checking is free for paragraphs without a label; 1 credit for each labelled paragraph found.
import os
import sys

import requests

API = "https://textwatermarking.com/api/watermark"
HEADERS = {"Authorization": f"Bearer {os.environ['TEXTWATERMARKING_API_TOKEN']}"}


def labelled_paragraphs(submission: str) -> list[tuple[int, str]]:
    """[(paragraph number, session id), ...] for paragraphs that contain a tutor answer."""
    found = []
    paragraphs = [p for p in submission.split("\n\n") if p.strip()]
    for number, paragraph in enumerate(paragraphs, start=1):
        r = requests.post(f"{API}/decode", json={"text": paragraph}, headers=HEADERS, timeout=10)
        if r.status_code == 422:           # a mark that is not a plain label: not from your tutor
            continue
        r.raise_for_status()
        label = r.json()["decoded"]
        if label.startswith("ai;tutor;s="):
            found.append((number, label.removeprefix("ai;tutor;s=")))
    return found


if __name__ == "__main__":
    # Usage: python check_submission.py < essay.txt
    for number, session in labelled_paragraphs(sys.stdin.read()):
        print(f"paragraph {number}: tutor session {session}")

Output such as paragraph 2: tutor session s-20931 points the teacher to the conversation. No output means no labelled paragraph was found.

What it costs

ItemValue
Label one tutor answer1 credit
Check one submissionfree for paragraphs without a label; 1 credit per labelled paragraph found
Example volume300 students × 20 answers a month = 6,000 credits (Pro: 25,000 a month)
Rate limit600 calls per 15 minutes per API token; contact us for a school-wide rollout

What it does not do

  • It does not detect other AI tools. Only your tutor's labelled answers are found.
  • It does not survive rewording. A student who rephrases the answer, retypes it or runs it through another model removes the label.
  • Some editors and file conversions drop the label. Test with the tools your students use, including the format submissions are uploaded in.
  • An excerpt that leaves out the first words of an answer has no label. The label sits at the start of each answer.

Frequently asked questions

Is this an AI detector?
No. It finds labels that your own tutor added to its answers. It says nothing about text from other AI tools, and it never guesses.
If no label is found, did the student write it alone?
Not necessarily. Rewording, retyping or another AI tool leaves no label. Absence of a label proves nothing either way.
Does the label contain personal data?
Only if you put it there. Use an opaque session id, as in the example. Anyone with a TextWatermarking API token can read a plain label, so never include names or student numbers.
Do word processors keep the label?
Most editors keep invisible characters when text is pasted, but some editors and file conversions drop them. Test with the tools your students actually use before you rely on it.
What does it cost?
One credit per tutor answer. Checking a submission is free for paragraphs without a label; each labelled paragraph found costs one credit. 300 students asking 20 questions a month is 6,000 credits, which fits the Pro plan.