Install our app for a better experience!

API Quickstart

API Quickstart — Evaluate Your First Feedback in 10 Minutes

This guide takes you from zero to a completed AI evaluation with downloadable reports. No prior knowledge of the platform is needed — every step shows the exact request to send and the exact response you will get back.

What the API does

You send us written teacher feedback (the comments a teacher wrote for a student). Our AI evaluates the quality of that feedback against a rubric and returns scores, strengths, and improvement suggestions — individually and as batch reports (PDF, Word, CSV, JSON).

The flow is always the same 5 steps:

1. Authenticate    2. Upload feedbacks    3. Create a batch    4. Run evaluation    5. Download reports

Base URL

All endpoints live under:

https://your-domain.com/assessment/api/

Replace your-domain.com with the domain you were given.

Prefer a machine-readable spec? Import our OpenAPI spec / Postman collection for every endpoint's fields, types, and enums — no need to read this guide end to end.

Step 0 — Get your credentials

Recommended: an API key. Organization admins can create keys themselves: open Evaluation Studio → API keys (/assessment/teacher-evaluation/api-keys/), give the key a name, and copy it — the key is shown once at creation and never again (only a hash is stored). Keys never expire, can be revoked from the same page, and are tied to your organization — you don't need to pass an organization_id with your requests.

Send it on every request in the X-API-Key header:

X-API-Key: YOUR_API_KEY

Alternative: username + password (session token). If you don't have an API key, you can log in and use the returned token. Note this token expires after ~2 weeks, so API keys are better for server-to-server integrations. See "Authentication options" at the end of this page.

Step 1 — Test your key

curl https://your-domain.com/assessment/api/organizations/ \
  -H "X-API-Key: YOUR_API_KEY"

Response:

{
  "success": true,
  "organizations": [
    {"id": 42, "name": "Your School", "role": "api_key", "created_at": "2026-01-01T00:00:00+00:00"}
  ]
}

If you get 401 with "code": "AUTH_INVALID_KEY", the key is wrong or has been revoked — check with your administrator.

Step 2 — Create (or pick) your rubric

Every evaluation is scored against a rubric — a set of criteria with points. Create one directly via the API:

curl -X POST https://your-domain.com/assessment/api/rubrics/create/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Feedback Quality Rubric",
    "description": "Evaluates clarity and actionability of teacher feedback",
    "criteria": [
      {"name": "Clarity", "description": "Is the feedback clear and specific?", "max_points": 10, "criterion_type": "score", "order": 1},
      {"name": "Actionability", "description": "Does it give actionable next steps?", "max_points": 10, "criterion_type": "score", "order": 2}
    ]
  }'
{
  "success": true,
  "message": "Rubric \"Feedback Quality Rubric\" created successfully",
  "rubric": {
    "id": 59,
    "name": "Feedback Quality Rubric",
    "total_possible_points": 20,
    "is_active": true,
    "criteria": [
      {"id": 305, "name": "Clarity", "max_points": 10, "criterion_type": "score", "order": 1},
      {"id": 306, "name": "Actionability", "max_points": 10, "criterion_type": "score", "order": 2}
    ]
  }
}

Notes: - criterion_type is one of score, binary, count, percentage (default score). - total_possible_points is computed automatically as the sum of the criteria's max_points. - Sending an empty criteria list, max_points: 0, or an unknown criterion_type returns 400 VALIDATION_ERROR telling you exactly which entry is wrong (e.g. criteria[1].max_points must be a positive integer).

Or list rubrics that already exist and note the id you want:

curl https://your-domain.com/assessment/api/rubrics/ \
  -H "X-API-Key: YOUR_API_KEY"

You can also read, edit, and retire a rubric:

# Full detail (criteria included)
curl https://your-domain.com/assessment/api/rubrics/59/ -H "X-API-Key: YOUR_API_KEY"

# Rename and/or replace the criteria set (completed evaluations are not affected)
curl -X PATCH https://your-domain.com/assessment/api/rubrics/59/ \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Feedback Quality Rubric v2"}'

# Deactivate — disappears from lists, existing batches and reports keep working
curl -X DELETE https://your-domain.com/assessment/api/rubrics/59/ -H "X-API-Key: YOUR_API_KEY"

Performance levels per criterion

Each criterion carries the four performance bands shown in the rubric UI — Excellent, Good, Satisfactory, Needs Improvement — with a point range and a description each. Every GET (list and detail) returns them on each criterion as a performance_levels array, in that fixed order:

{
  "id": 305,
  "name": "Clarity",
  "criterion_type": "score",
  "max_points": 10,
  "order": 1,
  "minimum_count": 0,
  "performance_levels": [
    {"level": "excellent",         "label": "Excellent",         "min_points": 9.0, "max_points": 10.0, "description": "Feedback is clear and specific throughout."},
    {"level": "good",              "label": "Good",              "min_points": 7.0, "max_points": 8.9,  "description": "Mostly clear, with minor lapses."},
    {"level": "satisfactory",      "label": "Satisfactory",      "min_points": 5.0, "max_points": 6.9,  "description": "Understandable but with noticeable ambiguity."},
    {"level": "needs_improvement", "label": "Needs Improvement", "min_points": 0.0, "max_points": 4.9,  "description": "Frequently unclear or vague."}
  ]
}

To set the bands when you create or update a rubric, include performance_levels (and minimum_count for count-based criteria) on the criterion. All four levels are optional — omit them to leave the bands blank:

{
  "name": "Clarity",
  "description": "Is the feedback clear and specific?",
  "max_points": 10,
  "criterion_type": "score",
  "order": 1,
  "performance_levels": [
    {"level": "excellent",         "min_points": 9, "max_points": 10,  "description": "Clear and specific throughout."},
    {"level": "good",              "min_points": 7, "max_points": 8.9, "description": "Mostly clear."},
    {"level": "satisfactory",      "min_points": 5, "max_points": 6.9, "description": "Some ambiguity."},
    {"level": "needs_improvement", "min_points": 0, "max_points": 4.9, "description": "Frequently unclear."}
  ]
}

Notes: - level must be one of excellent, good, satisfactory, needs_improvement; unknown levels are ignored. - label is read-only (returned for display; ignored on input). - Point ranges are advisory guidance for the AI evaluator — the criterion's max_points still governs the score ceiling.

Rubric-level guidance for the AI

A rubric can also carry overall guidance that steers every evaluation (this is the "Report Guidelines" panel in the web UI). Two fields are writable on create/update and returned on every GET:

  • custom_guidelines — your marking philosophy / what to prioritise.
  • custom_instructions — extra instructions for the evaluator.
curl -X PATCH https://your-domain.com/assessment/api/rubrics/59/ \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"custom_guidelines": "Reward specific, actionable feedback; be strict on vague praise.",
       "custom_instructions": "Always cite the criterion when scoring."}'

The GET response also returns ai_generated_guidelines (a richer, system-generated set of per-criterion guidelines and best practices) and guidelines_generated_at — both read-only (produced by the platform, not settable via the API).

Step 2b — Add reference examples (optional, improves consistency)

Reference examples calibrate the AI: you attach real feedback samples (marked excellent/good/average/poor) to a rubric, and evaluations of similar feedback are scored against them. Skip this on your first run — add references once you want scores tuned to your organization's standards.

# Create a reference example on rubric 59
curl -X POST https://your-domain.com/assessment/api/rubrics/59/references/create/ \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "title": "Excellent essay feedback example",
    "feedback_content": "Your thesis is clear and well supported. To improve, add citations in paragraph 2...",
    "quality_level": "excellent",
    "assessment_type": "essay",
    "evaluation_notes": "Specific, actionable, encouraging tone — this is what a top score looks like.",
    "key_strengths": ["Specific", "Actionable"],
    "key_weaknesses": []
  }'

# List / read / update / deactivate
curl https://your-domain.com/assessment/api/rubrics/59/references/ -H "X-API-Key: YOUR_API_KEY"
curl https://your-domain.com/assessment/api/references/12/ -H "X-API-Key: YOUR_API_KEY"
curl -X PATCH https://your-domain.com/assessment/api/references/12/ \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"quality_level": "good"}'
curl -X DELETE https://your-domain.com/assessment/api/references/12/ -H "X-API-Key: YOUR_API_KEY"

Field rules: title, feedback_content, quality_level, assessment_type, and evaluation_notes are required. quality_level must be one of excellent | good | average | poor; assessment_type one of essay | report | case_study | research | project | presentation | portfolio | other; key_strengths/key_weaknesses are lists of strings. Anything else returns 400 VALIDATION_ERROR with the exact problem. DELETE is a soft-delete — the example stops being used but past evaluations are untouched.

A semantic-search embedding is generated automatically on create (and re-generated when you change feedback_content); the response's embedding_generated field tells you if it succeeded. To USE references in an evaluation, set the RAG options when creating the batch (Step 4): "use_reference_matching": true (plus optional reference_matching_mode, reference_detail_level, max_references).

Step 3 — Upload feedbacks

Send the teacher feedback texts you want evaluated. title, feedback_content, and assessment_type are required for each one.

curl -X POST https://your-domain.com/assessment/api/feedbacks/bulk/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "feedbacks": [
      {
        "title": "Essay 1 feedback",
        "feedback_content": "Good structure and clear thesis. Work on citing sources and expanding your conclusion.",
        "teacher_name": "Jane Smith",
        "assessment_type": "essay",
        "subject_area": "English"
      }
    ]
  }'
{
  "success": true,
  "message": "Successfully created 1 feedbacks",
  "feedbacks": [{"id": 533, "title": "Essay 1 feedback", "teacher_name": "Jane Smith", "word_count": 15}]
}

Save the id values — you need them in the next step. (With an API key you may omit organization_id; it defaults to your key's organization.)

Step 4 — Create a batch

Group the uploaded feedbacks into a batch and pick the rubric:

curl -X POST https://your-domain.com/assessment/api/batches/create/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "March feedback review",
    "rubric_id": 59,
    "feedback_ids": [533]
  }'
{
  "success": true,
  "message": "Batch evaluation created successfully",
  "batch": {"id": 108, "name": "March feedback review", "status": "pending", "total_feedbacks": 1}
}

Step 5 — Run the evaluation

curl -X POST https://your-domain.com/assessment/api/batches/108/run/ \
  -H "X-API-Key: YOUR_API_KEY"

This request blocks until the evaluation finishes — typically 30 seconds to 3 minutes depending on batch size. Set your HTTP client timeout to at least 5 minutes.

{
  "success": true,
  "message": "Batch evaluation completed successfully. 1 feedbacks evaluated.",
  "batch_id": 108,
  "average_score": 6.8
}

Prefer not to wait? Use fire-and-forget mode. Pass {"async": true} and the call returns immediately with HTTP 202 while the evaluation runs in the background:

curl -X POST https://your-domain.com/assessment/api/batches/108/run/ \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"async": true}'
{
  "success": true,
  "message": "Batch evaluation started in background",
  "batch_id": 108,
  "status": "processing",
  "status_url": "/assessment/api/batches/108/status/"
}

Then poll the status endpoint every 10–15 seconds until status is "completed" or "failed":

curl https://your-domain.com/assessment/api/batches/108/status/ \
  -H "X-API-Key: YOUR_API_KEY"

If a batch fails, re-run the SAME batch — a failed batch keeps its feedbacks and configuration, and POST /run/ accepts batches in pending or failed state. Don't create a new batch. If you POST /run/ while it's already running you get 400 BATCH_INVALID_STATE ("Batch is already processing") — that's your signal to keep polling, not retry.

Once status is "completed", the same status response carries the full results — you do not need a second call to build a dashboard:

{
  "success": true,
  "batch": {
    "id": 108,
    "status": "completed",
    "ai_model": "GPT OSS 120B",
    "overall_teacher_assessment": {
      "overall_assessment": "Across the batch, feedback is specific and encouraging...",
      "identified_strengths": ["Consistent praise of effort", "..."],
      "areas_of_improvement": ["Vague on next steps", "..."],
      "actionable_recommendations": ["Add one concrete revision per comment", "..."]
    },
    "overall_consistency_score": 8.2,
    "consistency_analysis": "Scoring was consistent across the batch..."
  },
  "results": [
    {
      "id": 2230,
      "feedback_title": "Essay feedback — J. Doe",
      "teacher_name": "Ms. Smith",
      "overall_score": 17.5,
      "percentage": 87.5,
      "performance_level": "good",
      "overall_feedback": "The feedback is clear and actionable, with strong praise...",
      "criterion_scores": {"305": 9, "306": 8.5},
      "criterion_feedback": {"305": "Clarity is excellent...", "306": "Give one concrete next step..."},
      "strengths": ["Specific examples", "Encouraging tone"],
      "areas_for_improvement": ["Add measurable next steps"],
      "specific_recommendations": ["Reference the rubric band explicitly"],
      "consistency_notes": "In line with similar feedbacks in this batch.",
      "created_at": "2026-07-25T10:04:11Z"
    }
  ]
}

Key result fields: overall_feedback (the AI's written verdict), performance_level (excellent/good/satisfactory/needs_improvement, derived from percentage at the 90/70/50 thresholds), criterion_scores / criterion_feedback (keyed by criterion id), and the strengths / areas_for_improvement / specific_recommendations lists. The batch-level overall_teacher_assessment is the AI's synthesis across all feedbacks (empty {} until the batch completes). The same result shape is returned by the JSON report (report/?format=json) and by a single feedback's result (results/{result_id}/download/?format=json).

Step 6 — Download reports

# PDF report
curl -o report.pdf "https://your-domain.com/assessment/api/batches/108/report/?format=pdf" \
  -H "X-API-Key: YOUR_API_KEY"

# Also available: format=docx, format=csv, format=json

Summary statistics as JSON:

curl https://your-domain.com/assessment/api/batches/108/summary/ \
  -H "X-API-Key: YOUR_API_KEY"

Data retention: results are stored indefinitely — there is no expiry. You can retrieve status, reports, summaries, and individual results by batch_id at any time, unless someone in your organization explicitly deletes the batch from the web UI.

Complete example — Python

import requests

BASE = "https://your-domain.com/assessment/api"
HEADERS = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}

# 1. Upload feedbacks
r = requests.post(f"{BASE}/feedbacks/bulk/", headers=HEADERS, json={
    "feedbacks": [{
        "title": "Essay 1 feedback",
        "feedback_content": "Good structure and clear thesis. Work on citations.",
        "teacher_name": "Jane Smith",
        "assessment_type": "essay",
    }]
})
r.raise_for_status()
feedback_ids = [f["id"] for f in r.json()["feedbacks"]]

# 2. Create a rubric (or list existing ones with GET /rubrics/)
r = requests.post(f"{BASE}/rubrics/create/", headers=HEADERS, json={
    "name": "Feedback Quality Rubric",
    "criteria": [
        {"name": "Clarity", "description": "Clear and specific?", "max_points": 10},
        {"name": "Actionability", "description": "Actionable next steps?", "max_points": 10},
    ],
})
rubric_id = r.json()["rubric"]["id"]

# 3. Create the batch
r = requests.post(f"{BASE}/batches/create/", headers=HEADERS, json={
    "name": "My first batch",
    "rubric_id": rubric_id,
    "feedback_ids": feedback_ids,
})
batch_id = r.json()["batch"]["id"]

# 4. Run (blocks until complete — allow a generous timeout)
r = requests.post(f"{BASE}/batches/{batch_id}/run/", headers=HEADERS, timeout=300)
print(r.json())  # {"success": true, "average_score": ...}

# 5. Download the PDF report
pdf = requests.get(f"{BASE}/batches/{batch_id}/report/", headers=HEADERS,
                   params={"format": "pdf"})
open("report.pdf", "wb").write(pdf.content)

Complete example — JavaScript (Node 18+ / browser fetch)

const BASE = "https://your-domain.com/assessment/api";
const HEADERS = { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" };

async function evaluateFeedback() {
  // 1. Upload feedbacks
  let res = await fetch(`${BASE}/feedbacks/bulk/`, {
    method: "POST", headers: HEADERS,
    body: JSON.stringify({
      feedbacks: [{
        title: "Essay 1 feedback",
        feedback_content: "Good structure and clear thesis. Work on citations.",
        teacher_name: "Jane Smith",
        assessment_type: "essay",
      }],
    }),
  });
  const feedbackIds = (await res.json()).feedbacks.map(f => f.id);

  // 2. Create a rubric (or list existing ones with GET /rubrics/)
  res = await fetch(`${BASE}/rubrics/create/`, {
    method: "POST", headers: HEADERS,
    body: JSON.stringify({
      name: "Feedback Quality Rubric",
      criteria: [
        { name: "Clarity", description: "Clear and specific?", max_points: 10 },
        { name: "Actionability", description: "Actionable next steps?", max_points: 10 },
      ],
    }),
  });
  const rubricId = (await res.json()).rubric.id;

  // 3. Create the batch
  res = await fetch(`${BASE}/batches/create/`, {
    method: "POST", headers: HEADERS,
    body: JSON.stringify({ name: "My first batch", rubric_id: rubricId, feedback_ids: feedbackIds }),
  });
  const batchId = (await res.json()).batch.id;

  // 4. Run (blocks until complete)
  res = await fetch(`${BASE}/batches/${batchId}/run/`, { method: "POST", headers: HEADERS });
  console.log(await res.json());

  // 5. Get the JSON report
  res = await fetch(`${BASE}/batches/${batchId}/report/?format=json`, { headers: HEADERS });
  console.log(await res.json());
}

evaluateFeedback();

Error handling

Every error response has this shape — check success and branch on code:

{"success": false, "code": "ORG_ACCESS_DENIED", "error": "Access denied to this organization"}
HTTP code Meaning What to do
400 VALIDATION_ERROR Missing/invalid field or malformed JSON Fix the request body
400 BATCH_INVALID_STATE Batch already running/completed, or not completed yet Check batch status first
401 AUTH_REQUIRED No credentials sent Add X-API-Key header
401 AUTH_INVALID_KEY Wrong or revoked API key Verify the key with your admin
401 AUTH_INVALID_TOKEN Bad session token Re-authenticate
401 AUTH_EXPIRED Session token expired Re-authenticate (or switch to an API key)
401 AUTH_INVALID_CREDENTIALS Wrong username/password Check credentials
403 ORG_ACCESS_DENIED Resource belongs to a different organization Use resources from your own org
404 NOT_FOUND ID doesn't exist Check the ID
405 METHOD_NOT_ALLOWED Wrong HTTP method Check the endpoint's method
500 SERVER_ERROR Something failed on our side Retry; contact support if it persists
500 EMAIL_SEND_FAILED Email could not be delivered Check SMTP configuration / recipient

Retry guidance: it is always safe to retry GET requests and run/ (a completed batch just returns BATCH_INVALID_STATE). Retrying feedbacks/bulk/ after a network failure may create duplicates — check with GET /assessment/api/feedbacks/ first.

Authentication options (reference)

API key (recommended) Session token (legacy)
Header X-API-Key: <key> Authorization: Bearer <token>
How to get one Issued by your platform administrator POST /assessment/api/auth/token/ with {"username", "password"} — use the token field of the response
Expires Never (revocable by admin) ~2 weeks
Organization Implied by the key — organization_id optional Must pass organization_id where required
Best for Server-to-server integrations Quick manual testing

Next steps

  • API Core Workflow Guide — the full 8-step workflow including email delivery and use-case configuration.
  • Teacher Feedback Evaluation API (API overview) — complete endpoint reference with every parameter.
  • Evaluation Studio Email API — send result emails to teachers automatically.
  • RAG Configuration API Guide — improve evaluations with reference feedback matching.