Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.studyfetch.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

The Assignment Grader API allows you to submit student work (text or uploaded materials), define grading criteria via a rubric, and receive AI-generated scores and feedback for every criterion. It supports inline rubrics, reusable rubric templates, assignment content for source-based grading, and aggregate educator reports.

Key Features

  • Custom Rubrics: Define grading criteria with specific point values
  • Flexible Input: Grade from uploaded materials or direct text
  • Consistent Grading: AI ensures uniform application of rubric criteria
  • Objective Grading: Get numerical grades based on your rubric
  • Assignment Content: Provide source material (reading passages, case studies, prompts) that the AI uses as the authoritative reference when grading
  • User Tracking: Optional user ID for tracking grading history
  • Assignment Groups: Group submissions by assignment ID
  • Student Identification: Track submissions by student email or ID
  • Rubric Templates: Save and reuse common rubrics

Base URL

All endpoints are relative to your API prefix (defaults to api/v1):
https://<your-host>/api/v1/assignment-grader
For local development:
http://localhost:3001/api/v1/assignment-grader

Authentication

Every endpoint is protected by FlexibleAuthGuard, which accepts either:
MethodHeaderNotes
API keyx-api-key: <YOUR_API_KEY>Server-to-server. Resolves to the owning organization.
JWTAuthorization: Bearer <JWT>User session. Resolves to both organization and user.
If neither is present the request returns 401 Unauthorized.
Every record (graded assignment, rubric template) is scoped to the authenticated organizationId. You can only read/delete records that belong to your org.

Common Headers

HeaderRequiredValue
Content-TypeOn POST requestsapplication/json
x-api-keyYes (or Authorization)Your API key
AuthorizationYes (or x-api-key)Bearer <jwt>

Endpoint Summary

#MethodPathPurpose
1POST/assignment-grader/createGrade a new assignment
2GET/assignment-grader/getList all graded assignments for the org
3GET/assignment-grader/get/:idFetch one graded assignment by ID
4DELETE/assignment-grader/delete/:idDelete a graded assignment
5POST/assignment-grader/rubric-templatesCreate a rubric template
6GET/assignment-grader/rubric-templatesList all rubric templates
7GET/assignment-grader/rubric-templates/:idFetch one rubric template
8DELETE/assignment-grader/rubric-templates/:idDelete a rubric template
9GET/assignment-grader/educator-report/:assignmentIdAggregate report across all submissions for an assignmentId

Creating a Rubric Definition

Before grading assignments, you need to define a rubric with specific criteria. Each criterion has:
  • pointsPossible: The maximum points for this criterion (required)
  • title: The name of the criterion (required)
  • description: Detailed explanation of what’s being evaluated (optional but recommended)
// Define a comprehensive rubric
const rubricDefinition = {
  criteria: [
    {
      pointsPossible: 25,
      title: "Thesis Statement",
      description: "Clear, arguable thesis that addresses the prompt"
    },
    {
      pointsPossible: 30,
      title: "Evidence and Analysis",
      description: "Strong evidence with thorough analysis"
    },
    {
      pointsPossible: 25,
      title: "Organization",
      description: "Logical structure with smooth transitions"
    },
    {
      pointsPossible: 20,
      title: "Writing Mechanics",
      description: "Grammar, spelling, and citation format"
    }
  ],
  assignmentContent: "Optional — source material or reading passage" // Optional
};

// Total points: 100

Create Assignment Grader

POST /assignment-grader/create
Grade a new assignment by providing the rubric and content to grade. Returns the saved graded assignment with AI-generated scores and feedback.

Request Body

FieldTypeRequiredDescription
titlestringYesTitle of the assignment.
materialIdstringConditional*ID of an uploaded material (PDF, doc, EPUB, etc.) to grade.
textToGradestringConditional*Raw text content to grade.
rubricRubricConditional**Inline rubric.
rubricTemplateIdstringConditional**ID of a saved rubric template to use.
assignmentContentstringNoSource material / passage / instructions students were given. The AI uses this as the authoritative reference. See Assignment Content.
assignmentIdstringNoTag to group multiple submissions under one logical assignment (used by educator reports).
studentIdentifierstringNoStudent email or ID.
userIdstringNoUser ID for usage tracking (defaults to authenticated user, or anonymous).
modelstringNoAI model key (defaults to gpt-4o-mini-2024-07-18).
* You must provide exactly one of materialId or textToGrade. Providing both or neither returns 400 Bad Request. ** You must provide at least one of rubric or rubricTemplateId. An inline rubric must contain at least one criterion.

Example Request (curl)

curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Pop Quiz — Chapter 3",
    "textToGrade": "1. Photosynthesis turns sunlight into sugar.\n2. Plants give off oxygen.",
    "assignmentContent": "Chapter 3 covers photosynthesis, the process by which plants use sunlight, water, and carbon dioxide to produce glucose and oxygen...",
    "rubric": {
      "criteria": [
        { "title": "Defines photosynthesis", "description": "Mentions sunlight + sugar/glucose", "pointsPossible": 5 },
        { "title": "Mentions oxygen", "description": "Identifies oxygen as a byproduct", "pointsPossible": 5 }
      ]
    },
    "assignmentId": "BIO-CH3-QUIZ",
    "studentIdentifier": "jane.doe@school.edu"
  }'

Response — 201 Created

{
  "success": true,
  "gradedAssignment": {
    "_id": "665a1c0e8f3b1a0012abcdef",
    "title": "Pop Quiz — Chapter 3",
    "grade": 90,
    "rubric": {
      "attemptFeedback": "Strong response. You captured the core concepts...",
      "criteria": [
        {
          "title": "Defines photosynthesis",
          "description": "Mentions sunlight + sugar/glucose",
          "pointsAwarded": 5,
          "pointsPossible": 5,
          "feedback": "Correct — you mentioned sunlight and sugar."
        },
        {
          "title": "Mentions oxygen",
          "description": "Identifies oxygen as a byproduct",
          "pointsAwarded": 4,
          "pointsPossible": 5,
          "feedback": "Good. For full marks, mention that oxygen is released as a byproduct of the reaction."
        }
      ]
    },
    "organizationId": "664f...",
    "userId": "665b...",
    "textToGrade": "1. Photosynthesis turns sunlight...",
    "assignmentContent": "Chapter 3 covers photosynthesis...",
    "assignmentId": "BIO-CH3-QUIZ",
    "studentIdentifier": "jane.doe@school.edu",
    "createdAt": "2026-04-28T07:14:00.000Z",
    "updatedAt": "2026-04-28T07:14:00.000Z"
  }
}

SDK Examples

import StudyfetchSDK from '@studyfetch/sdk';

const client = new StudyfetchSDK({
  apiKey: 'your-api-key',
  baseURL: 'https://studyfetchapi.com',
});

// Use the rubric definition from above
const params = {
  title: "Essay on Climate Change",
  textToGrade: "Student essay content here...",
  rubric: rubricDefinition,           // Optional: Custom rubric definition
  rubricTemplateId: "template-123",   // Optional: Use existing rubric template
  assignmentContent: "The source material or reading passage students were given...", // Optional: Reference material for grading
  assignmentId: "assign-456",         // Optional: Group submissions
  studentIdentifier: "john.doe@example.com", // Optional: Student email or ID
  userId: "student123",               // Optional: For tracking
  model: "gpt-4.1-2025-04-14"         // Optional: AI model to use
};

const result = await client.v1.assignmentGrader.create(params);

console.log(`Success: ${result.success}`);
console.log(`Grade: ${result.gradedAssignment?.grade || 0}/100`);
console.log(`Assignment ID: ${result.gradedAssignment?._id}`);

Get All Assignment Graders

GET /assignment-grader/get
Returns every graded assignment for the authenticated organization.

Example Request (curl)

curl -X GET https://api.yourapp.com/api/v1/assignment-grader/get \
  -H "x-api-key: YOUR_API_KEY"

Response — 200 OK

An array of AssignmentGrader objects.

SDK Examples

const graders = await client.v1.assignmentGrader.getAll();

graders.forEach(grader => {
  console.log(`ID: ${grader._id}`);
  console.log(`Title: ${grader.title}`);
  console.log(`Grade: ${grader.grade}`);
  console.log(`Created: ${grader.createdAt}`);
  console.log(`Assignment ID: ${grader.assignmentId}`);
  console.log(`Student: ${grader.studentIdentifier}`);
});

Get Assignment Grader by ID

GET /assignment-grader/get/:id
Retrieve a specific graded assignment by its ID.
Path paramDescription
idAssignment grader document ID (_id).

Example Request (curl)

curl -X GET https://api.yourapp.com/api/v1/assignment-grader/get/665a1c0e8f3b1a0012abcdef \
  -H "x-api-key: YOUR_API_KEY"

Response — 200 OK

A single AssignmentGrader object. Returns 404 Not Found if no assignment matches the ID.

SDK Examples

const graderId = "grader_123456";
const grader = await client.v1.assignmentGrader.getByID(graderId);

console.log(`ID: ${grader._id}`);
console.log(`Assignment: ${grader.title}`);
console.log(`Grade: ${grader.grade}/100`);
console.log(`Student: ${grader.studentIdentifier}`);
console.log(`Assignment ID: ${grader.assignmentId}`);

Delete Assignment Grader

DELETE /assignment-grader/delete/:id
Delete a specific graded assignment by its ID.
Path paramDescription
idAssignment grader document ID.

Example Request (curl)

curl -X DELETE https://api.yourapp.com/api/v1/assignment-grader/delete/665a1c0e8f3b1a0012abcdef \
  -H "x-api-key: YOUR_API_KEY"

Response — 200 OK

{ "success": true, "message": "Assignment deleted successfully" }
Returns 404 Not Found if the assignment doesn’t exist or belongs to a different org.

SDK Examples

const graderId = "grader_123456";
await client.v1.assignmentGrader.delete(graderId);
console.log("Assignment grader deleted successfully");

Generate Educator Report

GET /assignment-grader/educator-report/:assignmentId
Aggregates statistics across every graded submission in your organization that shares the given assignmentId. Use this after grading multiple students for the same logical assignment.
Path paramDescription
assignmentIdThe assignmentId you tagged on each submission when calling POST /create.

Example Request (curl)

curl -X GET https://api.yourapp.com/api/v1/assignment-grader/educator-report/WATER-CYCLE-2026 \
  -H "x-api-key: YOUR_API_KEY"

Response — 200 OK

{
  "assignmentId": "WATER-CYCLE-2026",
  "title": "Water Cycle Reading Comprehension",
  "totalSubmissions": 27,
  "statistics": {
    "averageGrade": "82.4",
    "minGrade": "55.0",
    "maxGrade": "100.0",
    "standardDeviation": "11.7"
  },
  "gradeDistribution": { "A": 9, "B": 11, "C": 5, "D": 1, "F": 1 },
  "criteriaAnalysis": [
    { "title": "Q1 — Evaporation",  "avgScore": 4.6, "maxPossible": 5, "submissionCount": 27 },
    { "title": "Q4 — Groundwater",  "avgScore": 2.9, "maxPossible": 5, "submissionCount": 27 }
  ],
  "strengths": [
    { "title": "Q1 — Evaporation", "avgScore": "4.6", "maxPossible": 5, "performanceRatio": "92.0" }
  ],
  "weaknesses": [
    { "title": "Q4 — Groundwater", "avgScore": "2.9", "maxPossible": 5, "performanceRatio": "58.0" }
  ],
  "submissions": [
    {
      "id": "665a1c0e8f3b1a0012abcdef",
      "studentIdentifier": "jane.doe@school.edu",
      "grade": 90,
      "createdAt": "2026-04-28T07:14:00.000Z"
    }
  ]
}

Field Semantics

FieldMeaning
statistics.averageGradeMean of grade across all matching submissions, formatted to 1 decimal.
statistics.standardDeviationPopulation stddev of grade, 1 decimal.
gradeDistributionLetter-grade buckets — A: 90–100, B: 80–89, C: 70–79, D: 60–69, F: below 60.
criteriaAnalysisPer-criterion average score across all submissions.
strengthsCriteria where avgScore / maxPossible >= 0.80.
weaknessesCriteria where avgScore / maxPossible < 0.70.
submissionsFlat list of every submission feeding the report.
Returns 404 Not Found if no submissions match the assignmentId in your organization.

SDK Examples

const assignmentId = "assign-456";
const report = await client.v1.assignmentGrader.generateReport(assignmentId);

console.log("Assignment Report:");
console.log(`Title: ${report.title}`);
console.log(`Total Submissions: ${report.totalSubmissions}`);
console.log(`Average Grade: ${report.statistics.averageGrade}`);
console.log(`Max Grade: ${report.statistics.maxGrade}`);
console.log(`Min Grade: ${report.statistics.minGrade}`);
console.log(`Standard Deviation: ${report.statistics.standardDeviation}`);

// Grade distribution
console.log("Grade Distribution:");
console.log(`  A (90-100): ${report.gradeDistribution.A}`);
console.log(`  B (80-89): ${report.gradeDistribution.B}`);
console.log(`  C (70-79): ${report.gradeDistribution.C}`);
console.log(`  D (60-69): ${report.gradeDistribution.D}`);
console.log(`  F (0-59): ${report.gradeDistribution.F}`);

// Top performing criteria
console.log("Strengths:");
report.strengths.forEach(strength => {
  console.log(`  ${strength.title}: ${strength.avgScore}/${strength.maxPossible} (${strength.performanceRatio})`);
});

// Criteria needing improvement
console.log("Weaknesses:");
report.weaknesses.forEach(weakness => {
  console.log(`  ${weakness.title}: ${weakness.avgScore}/${weakness.maxPossible} (${weakness.performanceRatio})`);
});

Rubric Templates

Rubric templates are reusable rubrics scoped to your organization. They optionally store an assignmentContent so the same source material is automatically supplied every time the template is used.

Create Rubric Template

POST /assignment-grader/rubric-templates

Request Body

FieldTypeRequiredDescription
namestringYesDisplay name for the template.
descriptionstringNoNotes about when/how to use the template.
assignmentContentstringNoDefault source material applied to any grading request that uses this template (overridable per request).
criteriaRubricCriterion[]YesGrading criteria. Must contain at least one entry.

Example Request (curl)

curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Water Cycle — Reading Comprehension",
    "description": "Grades student answers about the water cycle reading passage",
    "assignmentContent": "The Water Cycle\n\nWater on Earth is constantly moving...",
    "criteria": [
      { "title": "Q1 — Evaporation",   "description": "Mentions sun heating water", "pointsPossible": 5 },
      { "title": "Q2 — Condensation",  "description": "Describes vapor cooling/clouds", "pointsPossible": 5 },
      { "title": "Q3 — Precipitation", "description": "Names ≥3 forms of precipitation", "pointsPossible": 5 }
    ]
  }'

Response — 201 Created

A RubricTemplate document.

SDK Examples

const templateParams = {
  name: "Standard Essay Rubric",
  description: "Basic 100-point essay grading rubric",
  assignmentContent: "Optional — default source material for this template", // Optional: used when no assignmentContent is provided at grading time
  criteria: [
    {
      pointsPossible: 25,
      title: "Thesis Statement",
      description: "Clear, arguable thesis that addresses the prompt"
    },
    {
      pointsPossible: 30,
      title: "Evidence and Analysis",
      description: "Strong evidence with thorough analysis"
    },
    {
      pointsPossible: 25,
      title: "Organization",
      description: "Logical structure with smooth transitions"
    },
    {
      pointsPossible: 20,
      title: "Writing Mechanics",
      description: "Grammar, spelling, and citation format"
    }
  ]
};

const template = await client.v1.assignmentGrader.rubricTemplates.create(templateParams);
console.log(`Template created: ${template._id}`);

List Rubric Templates

GET /assignment-grader/rubric-templates
Returns every rubric template for the authenticated organization, sorted newest first.

Example Request (curl)

curl -X GET https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
  -H "x-api-key: YOUR_API_KEY"

SDK Examples

const templates = await client.v1.assignmentGrader.rubricTemplates.list();

templates.forEach(template => {
  console.log(`Name: ${template.name}`);
  console.log(`Description: ${template.description}`);
  console.log(`Created: ${template.createdAt}`);
});

Get Rubric Template by ID

GET /assignment-grader/rubric-templates/:id
Path paramDescription
idRubric template _id.
Returns a single RubricTemplate. Returns 404 Not Found if not found in your organization.

SDK Examples

const templateId = "template-123";
const template = await client.v1.assignmentGrader.rubricTemplates.getByID(templateId);

console.log(`Template: ${template.name}`);
console.log(`Description: ${template.description}`);
console.log(`Criteria:`, template.criteria);

Delete Rubric Template

DELETE /assignment-grader/rubric-templates/:id

Response — 200 OK

{ "success": true, "message": "Rubric template deleted successfully" }
Returns 404 Not Found if the template doesn’t exist or belongs to a different org.

SDK Examples

const templateId = "template-123";
await client.v1.assignmentGrader.rubricTemplates.delete(templateId);
console.log("Rubric template deleted successfully");

Assignment Content

The assignmentContent field provides the AI grader with the original material students were working from — such as a reading passage, case study, or prompt. This is critical for accurate grading because the AI can verify student answers against the actual source instead of relying on its own knowledge.

Where You Can Set It

LocationWhen it’s used
Top-level field on POST /assignment-grader/createUsed for that single grading request
Inside the rubric object on POST /assignment-grader/createSame as above (top-level takes priority if both set)
On a rubric template (POST /assignment-grader/rubric-templates)Automatically used whenever that template is selected for grading, unless overridden at the request level

Priority Order

When assignmentContent is specified in multiple places, the following priority applies:
  1. Top-level assignmentContent on the grading request (highest priority)
  2. assignmentContent inside the rubric object on the grading request
  3. assignmentContent saved on the rubric template (if rubricTemplateId is used — lowest priority)
This means you can save a default passage on a rubric template and override it per-request when needed — for example, if you update a passage slightly for one section of students.

Usage Patterns

Every grading request has up to four pieces working together:
PieceWhat it isWhere it goes
Source materialThe essay, passage, case study, or prompt the student reads firstassignmentContent
QuestionWhat the student is asked to do or answerCriterion title and description
RubricThe grading criteria — what counts as a good answerrubric.criteria[] or a saved rubric template
Student answerThe student’s actual submissiontextToGrade or materialId
The AI grader reads all four, then scores the student’s answer against the rubric using the source material as the ground truth. Below are the most common patterns for combining them.

Pattern 1 — Reading Comprehension (Passage + Questions)

A teacher assigns a reading passage and asks students to answer questions about it. Each question becomes a rubric criterion, and the passage goes in assignmentContent.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Chapter 5 Reading Comprehension",
    "assignmentContent": "The Industrial Revolution began in Britain in the late 18th century. Factories replaced cottage industries, steam power transformed transportation, and urban populations surged as workers migrated from rural areas. Working conditions were often dangerous, with long hours and child labor common until reform acts were passed in the mid-19th century.",
    "textToGrade": "1. The Industrial Revolution started in Britain.\n2. Steam power was used for transportation.\n3. Working conditions were great because factories were new.\n4. Child labor ended because of the Reform Acts.",
    "rubric": {
      "criteria": [
        {
          "title": "Q1 — Origin",
          "description": "Where and when did the Industrial Revolution begin? Student should mention Britain and the late 18th century.",
          "pointsPossible": 5
        },
        {
          "title": "Q2 — Steam power",
          "description": "How did steam power change society? Student should mention transportation.",
          "pointsPossible": 5
        },
        {
          "title": "Q3 — Working conditions",
          "description": "Describe working conditions. Student should mention dangerous conditions, long hours, or child labor — NOT that conditions were good.",
          "pointsPossible": 5
        },
        {
          "title": "Q4 — Reform",
          "description": "What ended child labor? Student should reference reform acts in the mid-19th century.",
          "pointsPossible": 5
        }
      ]
    },
    "assignmentId": "HISTORY-CH5",
    "studentIdentifier": "student@school.edu"
  }'
The AI will flag Q3 as incorrect because the student said conditions were “great” — the passage says they were dangerous.

Pattern 2 — Essay / Open-Ended Writing (No Source Material)

When students write an original essay (no assigned reading), you don’t need assignmentContent at all. The rubric criteria describe what a good essay looks like.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Persuasive Essay — School Uniforms",
    "textToGrade": "I believe schools should require uniforms because they reduce bullying and help students focus on learning instead of fashion...",
    "rubric": {
      "criteria": [
        {
          "title": "Thesis Statement",
          "description": "Clear, arguable thesis in the introduction that takes a position on the topic.",
          "pointsPossible": 20
        },
        {
          "title": "Supporting Arguments",
          "description": "At least two distinct arguments supporting the thesis, each with reasoning or evidence.",
          "pointsPossible": 30
        },
        {
          "title": "Counterargument",
          "description": "Acknowledges and responds to at least one opposing viewpoint.",
          "pointsPossible": 20
        },
        {
          "title": "Organization & Clarity",
          "description": "Logical paragraph structure, clear transitions, and a conclusion that reinforces the thesis.",
          "pointsPossible": 20
        },
        {
          "title": "Grammar & Mechanics",
          "description": "Minimal spelling, punctuation, and grammar errors.",
          "pointsPossible": 10
        }
      ]
    },
    "assignmentId": "ESSAY-UNIFORMS",
    "studentIdentifier": "student@school.edu"
  }'

Pattern 3 — Case Study Analysis (Scenario + Specific Questions)

Give the AI a case study as assignmentContent, then define criteria that test whether the student applied the right frameworks or identified the right issues.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Business Ethics Case Study",
    "assignmentContent": "Case Study: GreenTech Corp discovered that one of their suppliers uses child labor. The supplier accounts for 40% of GreenTech raw materials and switching would increase costs by 25%. GreenTech CEO must decide whether to continue the relationship, find alternatives, or publicly disclose the issue. Stakeholders include shareholders, employees, the children involved, and consumers.",
    "textToGrade": "GreenTech should drop the supplier immediately because child labor is wrong. They can find another supplier easily.",
    "rubric": {
      "criteria": [
        {
          "title": "Stakeholder Identification",
          "description": "Student identifies at least 3 stakeholders from the case and explains how each is affected.",
          "pointsPossible": 10
        },
        {
          "title": "Ethical Framework",
          "description": "Student applies at least one ethical framework (utilitarian, deontological, virtue ethics, etc.) to analyze the dilemma.",
          "pointsPossible": 15
        },
        {
          "title": "Trade-off Analysis",
          "description": "Student acknowledges the cost/supply trade-off (25% cost increase, 40% of materials) rather than oversimplifying.",
          "pointsPossible": 10
        },
        {
          "title": "Recommendation Quality",
          "description": "Student provides a nuanced recommendation with implementation steps, not just a one-line answer.",
          "pointsPossible": 15
        }
      ]
    }
  }'
The AI will notice the student oversimplified — they didn’t acknowledge the trade-offs or apply an ethical framework.

Pattern 4 — Reusable Template for Repeated Assignments

When many students answer the same questions about the same material, save everything in a rubric template so you only define the rubric and passage once. Create the template once:
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Photosynthesis Quiz",
    "assignmentContent": "Photosynthesis is the process by which green plants convert sunlight, water, and carbon dioxide into glucose and oxygen. It occurs primarily in the leaves, within organelles called chloroplasts. The light-dependent reactions happen in the thylakoid membranes, while the Calvin cycle occurs in the stroma.",
    "criteria": [
      { "title": "Inputs", "description": "Student names sunlight, water, and CO2 as inputs.", "pointsPossible": 5 },
      { "title": "Outputs", "description": "Student names glucose and oxygen as outputs.", "pointsPossible": 5 },
      { "title": "Location", "description": "Student mentions chloroplasts or leaves.", "pointsPossible": 5 }
    ]
  }'
Grade each student — just send the answer:
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Photosynthesis Quiz",
    "rubricTemplateId": "TEMPLATE_ID",
    "assignmentId": "BIO-PHOTOSYNTHESIS",
    "studentIdentifier": "student@school.edu",
    "textToGrade": "Plants use sunlight and water to make food. This happens in the leaves."
  }'
The passage and rubric are pulled from the template automatically — no need to re-send them.

Choosing the Right Pattern

ScenarioUse assignmentContent?Use a template?
Students answer questions about a specific readingYesYes, if grading multiple students
Students write an original essay with no assigned readingNoYes, if the rubric is reused across assignments
One-off grading of a single submissionDepends on whether there’s source materialNo — use an inline rubric
Same assignment given to 30+ studentsYes (bake it into the template)Yes
Case study or lab report with reference materialYesOptional

Grading from Material

Grade an assignment that has already been uploaded as a material.
Important: Materials need time to process after upload. You must wait for the material to have status: 'active' before grading it. Attempting to grade a material that is still processing will fail.Tip: Use the createAndProcess method for text materials to avoid manual waiting - it automatically waits for processing to complete.
// First upload the assignment as a material
const material = await client.v1.materials.create({
  name: "Student Essay - John Doe",
  content: {
    type: "pdf",
    file: studentEssayFile
  },
  references: [
    {
      title: 'Assignment Instructions',
      url: 'https://example.com/assignment-instructions'
    }
  ] // Optional: source references
});

// Wait for material to finish processing
let materialStatus = 'processing';
while (materialStatus !== 'active') {
  const updatedMaterial = await client.v1.materials.retrieve(material._id);
  materialStatus = updatedMaterial.status;
  if (materialStatus === 'error') {
    throw new Error('Material processing failed');
  }
  if (materialStatus === 'processing') {
    await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
  }
}

// Then grade using the material ID
const gradeResult = await client.v1.assignmentGrader.create({
  rubric: rubricDefinition,
  title: "Essay Assignment",
  materialId: material._id,
  assignmentId: "assign-456",
  studentIdentifier: "john.doe@example.com"
});
For text assignments, use createAndProcess to automatically wait for processing:
// Create and process text material in one step
const material = await client.v1.materials.createAndProcess({
  content: { 
    type: 'text',
    text: studentEssayText
  },
  name: 'Student Essay - John Doe',
  references: [
    {
      title: 'Assignment Prompt',
      url: 'https://example.com/assignment-prompt'
    }
  ] // Optional: source references
});

// Material is immediately ready for grading
const gradeResult = await client.v1.assignmentGrader.create({
  rubric: rubricDefinition,
  title: "Essay Assignment",
  materialId: material._id,
  assignmentId: "assign-456",
  studentIdentifier: "john.doe@example.com"
});

Full Example: Passage Reading + Question Answering

This end-to-end walkthrough shows the full lifecycle: create a reusable template with a passage baked in, grade multiple students against it, then pull a class-wide educator report.

Step 1 — Create a Rubric Template with the Passage

curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "The Water Cycle — Reading Comprehension",
    "description": "Grades student answers about the water cycle passage",
    "assignmentContent": "The Water Cycle\n\nWater on Earth is constantly moving. The sun heats water in oceans, lakes, and rivers, causing it to evaporate into water vapor that rises into the atmosphere. As the vapor rises, it cools and condenses into tiny droplets that form clouds — a process called condensation. When enough droplets gather, they fall back to Earth as precipitation (rain, snow, sleet, or hail). Some precipitation flows into rivers and streams as runoff, eventually returning to the ocean. Some seeps into the ground and becomes groundwater, which feeds wells and springs. This continuous movement of water — from surface to atmosphere and back — is called the water cycle, or hydrological cycle.",
    "criteria": [
      { "title": "Q1 — Evaporation",   "description": "Mentions the sun heating bodies of water", "pointsPossible": 5 },
      { "title": "Q2 — Condensation",  "description": "Describes vapor cooling and forming droplets/clouds", "pointsPossible": 5 },
      { "title": "Q3 — Precipitation", "description": "Names ≥3 forms of precipitation from the passage", "pointsPossible": 5 },
      { "title": "Q4 — Groundwater",   "description": "Mentions groundwater, wells, or springs", "pointsPossible": 5 },
      { "title": "Q5 — Closed loop",   "description": "Conveys that the cycle has no beginning or end", "pointsPossible": 5 }
    ]
  }'
Capture _id from the response — that’s your TEMPLATE_ID.
const template = await client.v1.assignmentGrader.rubricTemplates.create({
  name: "The Water Cycle — Reading Comprehension",
  description: "Grades student answers about the water cycle reading passage",
  assignmentContent: "The Water Cycle\n\nWater on Earth is constantly moving. The sun heats water in oceans, lakes, and rivers, causing it to evaporate into water vapor that rises into the atmosphere. As the vapor rises, it cools and condenses into tiny droplets that form clouds — a process called condensation. When enough droplets gather, they fall back to Earth as precipitation (rain, snow, sleet, or hail). Some precipitation flows into rivers and streams as runoff, eventually returning to the ocean. Some seeps into the ground and becomes groundwater, which feeds wells and springs. This continuous movement of water — from surface to atmosphere and back — is called the water cycle, or hydrological cycle. It has no beginning or end; it is a closed loop that has been running for billions of years.",
  criteria: [
    {
      title: "Question 1 — Evaporation",
      description: "What causes water to evaporate? Student should mention the sun heating bodies of water.",
      pointsPossible: 5
    },
    {
      title: "Question 2 — Condensation",
      description: "Explain what condensation is. Student should describe water vapor cooling and forming droplets/clouds.",
      pointsPossible: 5
    },
    {
      title: "Question 3 — Precipitation Types",
      description: "Name at least 3 forms of precipitation mentioned in the passage.",
      pointsPossible: 5
    },
    {
      title: "Question 4 — Groundwater",
      description: "What happens to precipitation that seeps into the ground? Student should mention groundwater, wells, or springs.",
      pointsPossible: 5
    },
    {
      title: "Question 5 — Why is it called a cycle?",
      description: "Why is the water cycle described as a closed loop? Student should convey that it has no beginning or end and repeats continuously.",
      pointsPossible: 5
    }
  ]
});

console.log(`Template created: ${template._id}`);

Step 2 — Grade Each Student’s Submission

Repeat this for every student, keeping the same assignmentId.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Water Cycle Reading Comprehension",
    "rubricTemplateId": "TEMPLATE_ID",
    "assignmentId": "WATER-CYCLE-2026",
    "studentIdentifier": "jane.doe@school.edu",
    "textToGrade": "1. Water evaporates because the sun heats it...\n2. Condensation is when vapor turns into clouds...\n3. Rain, snow, sleet.\n4. It becomes part of rivers.\n5. It just keeps going."
  }'
const result = await client.v1.assignmentGrader.create({
  title: "Water Cycle Reading Comprehension",
  rubricTemplateId: template._id,  // Reuse the template from Step 1
  assignmentId: "WATER-CYCLE-2026",
  studentIdentifier: "jane.doe@school.edu",
  textToGrade: "1. Water evaporates because the sun heats it up from oceans and lakes.\n2. Condensation is when water vapor goes up and turns into clouds.\n3. Rain, snow, and sleet.\n4. It goes into the ground and becomes part of rivers.\n5. It is called a cycle because it keeps going around and around without stopping."
});

console.log(`Grade: ${result.gradedAssignment.grade}/100`);

// Per-criterion feedback
result.gradedAssignment.rubric.criteria.forEach(c => {
  console.log(`${c.title}: ${c.pointsAwarded}/${c.pointsPossible}`);
  console.log(`  Feedback: ${c.feedback}`);
});
What the AI grader does:
  1. Reads the rubric criteria (questions + what to look for)
  2. Reads the assignment content (the water cycle passage) as the source of truth
  3. Reads the student’s submission (their answers)
  4. Grades each answer against the passage, not against its own knowledge
  5. Returns per-criterion scores and feedback

Step 3 — Pull the Educator Report

After grading multiple students with the same assignmentId, generate an aggregate report:
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/educator-report/WATER-CYCLE-2026 \
  -H "x-api-key: YOUR_API_KEY"
You’ll receive aggregate statistics, grade distribution, per-criterion analysis, strengths, weaknesses, and a list of every submission.
const report = await client.v1.assignmentGrader.generateReport("WATER-CYCLE-2026");

console.log(`Average Grade: ${report.statistics.averageGrade}`);
console.log(`Total Submissions: ${report.totalSubmissions}`);

// See which criteria students struggled with
report.weaknesses.forEach(w => {
  console.log(`Weakness: ${w.title} — avg ${w.avgScore}/${w.maxPossible}`);
});

Inline Rubric with Assignment Content (No Template)

If you don’t need a reusable template, you can pass everything in a single request:
const result = await client.v1.assignmentGrader.create({
  title: "Pop Quiz — Chapter 3",
  assignmentContent: "Chapter 3 discusses the three branches of the US government: the Legislative branch (Congress), the Executive branch (the President), and the Judicial branch (the Supreme Court). The Legislative branch makes laws, the Executive branch enforces laws, and the Judicial branch interprets laws...",
  textToGrade: "1. The three branches are Legislative, Executive, and Military.\n2. Congress makes the laws.\n3. The Supreme Court enforces the laws.",
  rubric: {
    criteria: [
      {
        title: "Three Branches",
        description: "Student correctly names all three branches of government",
        pointsPossible: 10
      },
      {
        title: "Legislative Role",
        description: "Student explains what the Legislative branch does",
        pointsPossible: 5
      },
      {
        title: "Judicial Role",
        description: "Student explains what the Judicial branch does",
        pointsPossible: 5
      }
    ]
  }
});

// The AI will catch that the student said "Military" instead of "Judicial"
// and mixed up what the Supreme Court does
console.log(`Grade: ${result.gradedAssignment.grade}`);

Object Reference

RubricCriterion Object

FieldTypeRequiredDescription
titlestringYesShort name of the criterion.
descriptionstringNoWhat you want graded. Written as instructions for the grader.
pointsPossiblenumberYesMaximum points this criterion can earn.

Rubric Object (Request Input)

FieldTypeRequiredDescription
criteriaRubricCriterion[]YesMust have ≥ 1 entry.
assignmentContentstringNoOptional source material (superseded by top-level assignmentContent).

GradedRubricCriterion Object (Response)

FieldTypeDescription
titlestringMirrors the input criterion title.
descriptionstringMirrors the input criterion description.
pointsAwardednumberPoints the AI awarded.
pointsPossiblenumberMaximum points for this criterion.
feedbackstringPer-criterion feedback (second-person, addressed to the student).

AssignmentGrader Object

FieldTypeDescription
_idstring (ObjectId)Document ID.
titlestringAssignment title.
gradenumberOverall percentage (0–100), rounded to nearest whole number.
rubric.attemptFeedbackstringHolistic feedback for the whole submission.
rubric.criteriaGradedRubricCriterion[]Per-criterion results.
organizationIdstringOwning organization.
userIdstring | nullUser who triggered the grading (or null for API-key auth).
materialIdstring?Material that was graded (mutually exclusive with textToGrade).
textToGradestring?Raw text that was graded.
rubricTemplateIdstring?Template used (if any).
assignmentIdstring?Grouping tag for educator reports.
studentIdentifierstring?Student email or ID.
assignmentContentstring?Source material the AI used.
createdAtDateTimestamp.
updatedAtDateTimestamp.

RubricTemplate Object

FieldTypeDescription
_idstring (ObjectId)Template ID.
namestringTemplate name.
descriptionstring?Optional description.
assignmentContentstring?Default source material attached to this template.
criteriaRubricCriterion[]Grading criteria.
organizationIdstringOwning organization.
createdBystringUser who created the template.
createdAtDateTimestamp.
updatedAtDateTimestamp.

Error Responses

All errors follow the standard exception envelope.
StatusWhen
400 Bad RequestInvalid body — missing required fields, both materialId and textToGrade supplied, neither rubric nor template provided, empty rubric, or unknown grader action.
401 UnauthorizedMissing or invalid x-api-key / Authorization header.
404 Not FoundAssignment, template, or report target not found in your organization.
500 Internal Server ErrorUnexpected failure (AI service error, DB failure, etc.).
Example error body:
{
  "statusCode": 400,
  "message": "Must provide either materialId or textToGrade",
  "error": "Bad Request"
}

Tips & Best Practices

  • Provide assignmentContent whenever possible. It dramatically improves grading accuracy because the AI grades against the actual source instead of guessing.
  • Keep assignmentContent focused. Paste the actual passage or prompt students received. Don’t include instructions meant for the grader — put those in the criterion description fields instead.
  • Use criterion descriptions as grading instructions. For example: “Award full points only if the student names ≥2 specific dates from the passage.”
  • Save templates for repeated assignments. If 30 students are all answering the same reading comprehension, create a template once and reuse it with rubricTemplateId.
  • Always set assignmentId and studentIdentifier when you plan to run an educator report. Without assignmentId, the report can’t aggregate submissions.
  • Top-level wins for assignmentContent. If you need a one-off override of the template’s saved content, just include assignmentContent at the top level of the grading request.
  • Cap on inputs. Materials larger than ~30k chars per item are truncated for grading; very large materials (>100k chars) are also truncated when extracted.
  • Strict grading. The grader awards 0 points for factually incorrect answers. Post-processing also detects keywords like “incorrect”, “wrong”, “opposite” in the AI’s per-criterion feedback and forces pointsAwarded to 0 (unless the feedback also indicates partial correctness with words like “partially”, “mostly”).