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.
