Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vruizz22/innova-backend-serverless/llms.txt

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

The Attempts API is the core ingestion endpoint for the Innova platform. POST /attempts accepts a student’s answer along with intermediate work steps, runs them synchronously through the rule engine, applies a Bayesian Knowledge Tracing (BKT) mastery update for the matched topic, and enqueues a telemetry event to the SQS FIFO stream. When the rule engine cannot classify an error, the attempt is also forwarded to the async LLM classifier — poll GET /attempts/:id/status for the final result.

POST /attempts

Requires a valid JWT in the Authorization: Bearer header. Pass an optional x-trace-id header to correlate this attempt with your own observability pipeline; a UUID is generated automatically when omitted.
Ingest one student attempt. The rule engine runs synchronously and the response is returned immediately. If errorTagCode is UNCLASSIFIED in the response the attempt has also been forwarded to the LLM classifier — use GET /attempts/:id/status to poll for the upgraded tag. POST /attempts

Request body

studentId
string
required
The student’s profile UUID.
topicCode
string
required
Curriculum topic code that the exercise belongs to (e.g. T-SUB-BORROW). Used to route the rule engine and to look up the BKT parameters for the mastery update.
exerciseId
string
UUID of the Exercise record. Optional — ad-hoc scans submitted without a guide context may omit this.
courseId
string
UUID of the course. Optional, but required for BKT mastery to be scoped correctly when a student is enrolled in multiple courses.
rawSteps
AttemptStepDto[]
required
Ordered array of intermediate work steps the student wrote.
expectedAnswer
number
required
The correct numeric answer to the exercise (e.g. 27). Used by the rule engine to determine correctness.
studentAnswer
number
required
The numeric answer the student submitted (e.g. 33).
minuend
number
The minuend operand (e.g. 53). Optional — improves subtraction-specific rule matching when provided.
subtrahend
number
The subtrahend operand (e.g. 26). Optional — improves subtraction-specific rule matching when provided.

Response 201

{
  "attemptId": "uuid",
  "isCorrect": false,
  "errorTagCode": "BORROW_OMITTED",
  "classifierSource": "RULE",
  "confidence": 0.92
}
attemptId
string
UUID of the newly created attempt record.
isCorrect
boolean
true when the student’s answer equals expectedAnswer.
errorTagCode
string
The matched error tag code from the catalog (e.g. BORROW_OMITTED, CORRECT, or UNCLASSIFIED).
classifierSource
string
RULE when the rule engine produced the tag; LLM when the attempt was forwarded to the async LLM worker (only set to LLM on UNCLASSIFIED attempts — the tag will be upgraded after async processing).
confidence
number
Classifier confidence score between 0 and 1.
When errorTagCode is UNCLASSIFIED, classifierSource will be LLM and the attempt status will be PENDING. The attempt has been enqueued for async LLM classification. Poll GET /attempts/:id/status until status is CLASSIFIED.

Example

curl -X POST https://api.innova.app/attempts \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "studentId": "stu_abc123",
    "topicCode": "T-SUB-BORROW",
    "exerciseId": "ex_xyz789",
    "courseId": "crs_def456",
    "rawSteps": [
      { "expression": "53 - 26", "isFinal": false },
      { "expression": "53 - 26 = 33", "isFinal": true }
    ],
    "expectedAnswer": 27,
    "studentAnswer": 33,
    "minuend": 53,
    "subtrahend": 26
  }'

POST /attempts/solve-adhoc

Create a PENDING attempt for an ad-hoc scan that has no guide context. Use this when the student scanned an exercise whose expected answer cannot be derived client-side (e.g. symbolic algebra). The attempt is enqueued for the adhoc_solver worker, which runs full-mode solution generation and writes the classification back to the attempt row. Poll GET /attempts/:id/status for the result. POST /attempts/solve-adhoc

Request body

studentId
string
required
The student’s profile UUID.
problemLatex
string
required
LaTeX string of the problem statement as extracted from the OCR scan.
studentFinalAnswer
string
required
The student’s final answer, which may be symbolic (e.g. x=2).
studentSteps
string[]
Ordered array of LaTeX strings for each intermediate step the student wrote. Optional but improves classification accuracy.
courseId
string
Course UUID for mastery context. Optional.
gradeLevel
integer
Grade level between 1 and 12. Defaults to 7 when not provided.

Response 201

{
  "attemptId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
attemptId
string
UUID of the created attempt. Use this to poll GET /attempts/:id/status.
The worker processes ad-hoc attempts asynchronously. Typical turnaround is under 5 seconds. Implement exponential backoff with a cap of 30 seconds when polling.

POST /attempts/ocr-extract

Extract math steps from a handwritten image using the platform’s OCR pipeline. Accepts a multipart/form-data upload and returns structured exercise data you can feed directly into POST /attempts or POST /attempts/solve-adhoc. POST /attempts/ocr-extract

Request

Content-Type: multipart/form-data
image
binary
required
The handwritten image file (JPEG or PNG).

Response 201

{
  "exercises": [
    {
      "problem": "53 - 26",
      "rawSteps": [
        { "expression": "53 - 26 = 33", "isFinal": true }
      ],
      "finalAnswer": "33",
      "topicHint": "T-SUB-BORROW",
      "confidence": 0.91
    }
  ]
}
exercises
OcrExtractExercise[]
Array of extracted exercises. A single image may contain more than one exercise.

Example

curl -X POST https://api.innova.app/attempts/ocr-extract \
  -H "Authorization: Bearer <token>" \
  -F "image=@/path/to/student_work.jpg"

GET /attempts/:id/status

Poll the live classification status of an attempt. Intended for use after POST /attempts when the response contains errorTagCode: "UNCLASSIFIED", or after POST /attempts/solve-adhoc while the adhoc worker processes the problem. GET /attempts/:id/status

Path parameters

id
string
required
The attempt UUID returned by the submit endpoint.

Response 200

{
  "attemptId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "CLASSIFIED",
  "isCorrect": false,
  "errorTagCode": "SUB_BORROW_NO_REGROUP",
  "errorTagName": "Resta sin reagrupar",
  "classifierSource": "LLM",
  "confidence": 0.87
}
attemptId
string
The attempt UUID.
status
string
PENDING while the async worker is processing; CLASSIFIED once a final error tag has been written.
isCorrect
boolean
Whether the student’s answer was correct.
errorTagCode
string | null
The final classified error tag code. null while still PENDING.
errorTagName
string | null
Human-readable error tag name. null while still PENDING.
classifierSource
string
RULE or LLM — which classifier produced the final tag.
confidence
number | null
Confidence score. null while PENDING.

GET /attempts/:id/detail

Full attempt detail including all work steps and presigned photo URLs. Use this for the student drilldown view or to render the lightbox of scanned work. GET /attempts/:id/detail

Path parameters

id
string
required
The attempt UUID.

Response 200

attemptId
string
The attempt UUID.
status
string
PENDING or CLASSIFIED.
isCorrect
boolean
Whether the attempt was correct.
errorTagCode
string | null
Classified error tag code.
errorTagName
string | null
Classified error tag display name.
classifierSource
string
RULE or LLM.
confidence
number | null
Classifier confidence.
steps
AttemptStepView[]
Ordered array of the student’s work steps.
submission
object | null
Present when the attempt originated from a guide submission scan.

POST /attempts/:id/report

Field-report the correct error tag for an attempt (teacher feedback loop, v8 C4). Submitting a report does not overwrite the original classification — it creates a separate AttemptErrorReport record used to improve the classifier. Students and teachers may both call this endpoint. POST /attempts/:id/report

Path parameters

id
string
required
The attempt UUID.

Request body

errorTagCode
string
required
The correct error tag code from the catalog (e.g. SUB_BORROW_NO_REGROUP). Must match an existing error tag.
comment
string
Optional free-text note from the reporter (max 500 characters).

Response 201

{
  "attemptId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "reported": true
}
attemptId
string
The attempt UUID.
reported
boolean
Always true on success.

Example

curl -X POST https://api.innova.app/attempts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/report \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "errorTagCode": "SUB_BORROW_NO_REGROUP",
    "comment": "Student skipped regrouping entirely, not a simple omission."
  }'

Build docs developers (and LLMs) love