Developer API
Add AI marking to your product in an afternoon
A small REST API with signed webhooks. Send written answers with a rubric; get back marks per criterion, highlight positions and reasons you can show teachers straight away.
Getting connected
1 · Get a key
We create a project for you and send its API key privately. Check it with GET /v1/me.
2 · Register your webhook
PUT /v1/webhook with your URL; you receive a signing secret for verifying results.
3 · Submit answers
POST /v1/evaluations per question. Results arrive at your webhook, or sync them with GET.
Submit answers, get structured results
Answers are idempotent per externalId, so retrying a request never marks twice. Use your exam ID as the groupId to follow progress and costs per exam.
POST https://api.scholametrix.tech/v1/evaluations
Authorization: Bearer $SCHOLAMETRIX_API_KEY
Content-Type: application/json
{
"groupId": "cse-3101-midterm",
"question": {
"externalId": "q2",
"text": "Explain how TCP provides reliable delivery.",
"modelAnswer": "Sequence numbers, cumulative ACKs, retransmission…",
"maxMarks": 10,
"markStep": 0.5,
"rubric": {
"criteria": [
{ "id": "c1", "criterion": "Sequence numbers", "marks": 2 },
{ "id": "c2", "criterion": "Acknowledgements", "marks": 2 }
]
}
},
"answers": [
{ "externalId": "ans-1029", "text": "TCP gives each byte a sequence number…" }
]
}The result
Highlights are character offsets into the answer text, ready to render.
{
"id": "1c0d…",
"externalId": "ans-1029",
"groupId": "cse-3101-midterm",
"status": "completed",
"maxMarks": 10,
"suggestedScore": 8,
"criteria": [
{ "id": "c1", "criterion": "Sequence numbers", "max": 2, "awarded": 2,
"reason": "States sequence numbers allow reordering and detecting missing data." },
{ "id": "c5", "criterion": "Flow control", "max": 2, "awarded": 0,
"reason": "Does not mention flow control or a sliding window." }
],
"summary": "Explains sequence numbers, ACKs, retransmission and checksum, but omits flow control.",
"highlights": [
{ "start": 0, "end": 133, "kind": "strength", "criterionId": "c1" }
],
"missing": [{ "criterionId": "c5", "note": "No mention of flow control or sliding window." }],
"review": null
}Reference
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /v1/me | Check your key; see your project, plan and this month’s usage |
| PUT | /v1/webhook | Register where results are sent; returns your signing secret |
| POST | /v1/rubrics/draft | Draft a rubric from a question and model answer |
| POST | /v1/evaluations | Submit one question with up to 500 answers |
| POST | /v1/evaluations/try | Mark one sample answer straight away (nothing stored) |
| GET | /v1/evaluations/:id | One result: criterion marks, highlights, reasons |
| GET | /v1/evaluations | List results by group, updated since a time (for sync) |
| GET | /v1/groups/:groupId/progress | How far marking of an exam has got |
| POST | /v1/evaluations/:id/review | Record the teacher’s decision and final marks |
| POST | /v1/evaluations/:id/rerun | Mark an answer again |
| GET | /v1/usage | Answers and rubric drafts for a period |
| GET | /v1/billing | Your charges per month, split by your groups |
Webhooks
Signed, retried, safe to replay
- Every delivery carries X-AMS-Signature: an HMAC-SHA256 over the timestamp and raw body.
- Reply 2xx within 10 seconds; failures retry with exponential backoff, up to 8 attempts.
- Deliveries can repeat or arrive out of order, so upsert by evaluation id.
- Prefer polling? GET /v1/evaluations?groupId=…&updatedSince=… returns the same data.
import { createHmac, timingSafeEqual } from 'node:crypto';
// X-AMS-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
export function verify(secret: string, rawBody: string, header?: string) {
if (!header) return false;
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? '', 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}Limits
Generous defaults
- Answers per request
- 500
- Characters per answer
- 20,000
- Highlights per answer
- Up to 8
- Rubric criteria
- Up to 12
Full API documentation is shared with every project. Ask for access.
Building an LMS or exam product?
Tell us about your platform. We’ll set up a sandbox project and help you ship AI marking to your users.