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 Guides API manages the full teacher-worksheet lifecycle, from PDF upload to student results. A teacher creates a guide, receives a presigned S3 URL to upload the PDF, triggers extraction, reviews and edits the extracted questions in the wizard UI, and finally publishes — which materialises exercises and creates an assignment for all enrolled students. All endpoints require the TEACHER role and a valid JWT.

POST /guides

Create a new guide record and receive a presigned S3 PUT URL for uploading the worksheet PDF. The guide starts in UPLOADED status. Upload the PDF directly to the returned URL, then call POST /guides/:id/ingest to start extraction. POST /guides

Request body

courseId
string
required
UUID of the course this guide belongs to. The authenticated teacher must be assigned to this course.
title
string
required
Guide title (e.g. Guía 3 — Fracciones).
description
string
Optional description shown to the teacher in the guides list.
fileName
string
Original PDF filename. Accepted by the API but not persisted — reserved for future content-type inference.
dueAt
string
ISO 8601 datetime string for the due date (e.g. 2025-09-15T23:59:00Z). Optional — guides without a due date remain open indefinitely.

Response 201

{
  "guideId": "gd_abc123",
  "presignedPutUrl": "https://s3.amazonaws.com/innova-guides/guides/uploads/uuid.pdf?X-Amz-...",
  "sourcePdfKey": "guides/uploads/uuid.pdf"
}
guideId
string
UUID of the newly created guide.
presignedPutUrl
string
S3 presigned PUT URL. Upload the PDF with Content-Type: application/pdf. The URL expires after the configured TTL (default: 10 minutes).
sourcePdfKey
string
The S3 object key where the PDF will be stored. Keep this for reference; it is also returned by GET /guides/:id/source-url.

Example

# 1. Create the guide
curl -X POST https://api.innova.app/guides \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "courseId": "crs_def456",
    "title": "Guía 3 — Fracciones",
    "dueAt": "2025-09-15T23:59:00Z"
  }'

# 2. Upload the PDF directly to S3
curl -X PUT "<presignedPutUrl>" \
  -H "Content-Type: application/pdf" \
  --data-binary @guia3.pdf

GET /guides

List the authenticated teacher’s guides with optional filters. Results are scoped to courses the teacher leads or assists. Guides that have been archived (DELETE /guides/:id) are excluded. GET /guides

Query parameters

courseId
string
Filter by course UUID. Must belong to the teacher.
status
string
Filter by guide status. One of: UPLOADED, EXTRACTING, EXTRACTION_FAILED, GENERATING_SOLUTIONS, GENERATION_FAILED, REVIEW, PUBLISHED, ARCHIVED.
page
number
1-based page number. Defaults to 1.
pageSize
number
Items per page. Defaults to 20, maximum 100.

Response 200

{
  "items": [ { "id": "gd_abc123", "title": "Guía 3 — Fracciones", "status": "REVIEW", "_count": { "questions": 8, "submissions": 0 } } ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
items
Guide[]
Array of guide records, ordered by creation date descending. Each item includes a _count object with questions and submissions counts.
total
number
Total matching guides (for pagination controls).
page
number
Current page number.
pageSize
number
Items returned per page.

GET /guides/:id

Full guide detail for the wizard UI: the guide record, its extracted questions in sequence order, and each question’s current solution (where one exists). GET /guides/:id

Path parameters

id
string
required
Guide UUID.

Response 200

Returns the guide object with a questions array. Each question includes:
  • topic — resolved curriculum topic (id, code, name)
  • domain — resolved taxonomy domain (id, code, name)
  • subdomain — resolved taxonomy subdomain (id, code, name)
  • solutions — array with the single current solution (empty if not yet generated)

PATCH /guides/:id

Update guide metadata. All fields are optional — only provided fields are updated. PATCH /guides/:id

Path parameters

id
string
required
Guide UUID.

Request body

title
string
Updated guide title.
description
string
Updated description.
dueAt
string
Updated ISO 8601 due date.
maxResubmissions
number
Maximum number of re-submissions allowed per student. Must be 0 or greater.
showSolutionAfterGrade
boolean
When true, the canonical solution is revealed to the student after their submission is graded.

Response 200

Returns the updated guide record.

POST /guides/:id/ingest

Start (or retry) PDF extraction for the uploaded guide. Returns 202 Accepted immediately — extraction runs asynchronously. The guide status moves to EXTRACTING and then to GENERATING_SOLUTIONS once the extractor completes successfully. POST /guides/:id/ingest
This endpoint is idempotent for in-progress states: calling it while the guide is already EXTRACTING or GENERATING_SOLUTIONS returns the current status without re-enqueueing. Call it to retry after EXTRACTION_FAILED or GENERATION_FAILED.

Path parameters

id
string
required
Guide UUID.

Response 202

{ "status": "EXTRACTING" }
status
string
The new guide status after the enqueue.

Example

curl -X POST https://api.innova.app/guides/gd_abc123/ingest \
  -H "Authorization: Bearer <token>"

PATCH /guides/:id/questions/:qid

Edit a question, confirm its taxonomy classification, or set its review status to APPROVED or EXCLUDED. Only APPROVED and EXCLUDED statuses may be set by the teacher — EXTRACTED and NEEDS_REVIEW are assigned by the pipeline (ADR-119). PATCH /guides/:id/questions/:qid

Path parameters

id
string
required
Guide UUID.
qid
string
required
Question UUID.

Request body

statementLatex
string
Corrected LaTeX for the question statement.
label
string
Display label for the question (e.g. 1.a).
points
number
Points value for this question. Must be 0 or greater.
subdomainId
string
UUID of the confirmed taxonomy subdomain. Preferred since v9.1 — sets domainId automatically from the subdomain and marks topicSource as TEACHER.
topicId
string
UUID of the confirmed curriculum topic (legacy). Use subdomainId for new integrations.
status
string
Question review status. Must be APPROVED or EXCLUDED.

Response 200

Returns the updated question record.

PATCH /guides/:id/questions/:qid/solution

Save an edited solution for a question. Each save creates a new GuideSolution version with source = TEACHER_EDITED and marks it as isCurrent = true, demoting the previous version. The stepsJson is validated against the canonical solution schema (ADR-118) and must contain at least one checkpoint step. PATCH /guides/:id/questions/:qid/solution

Path parameters

id
string
required
Guide UUID.
qid
string
required
Question UUID.

Request body

finalAnswer
string
required
The correct final answer as a string (e.g. "27").
stepsJson
object
required
Canonical solution object (ADR-118). Must conform to the schema below and include at least one step with checkpoint: true.
solutionLatex
string
Derived LaTeX render of the full solution. Optional — used for display only, not the source of truth.
expectedErrorTags
string[]
Array of error tag codes this question is designed to surface (e.g. ["BORROW_OMITTED", "SUB_BORROW_NO_REGROUP"]).

Response 200

Returns the newly created GuideSolution record.
If stepsJson fails schema validation or contains no checkpoint step the request returns 400 Bad Request with a description of the validation error. The existing solution is left unchanged.

POST /guides/:id/questions/:qid/regenerate-solution

Re-enqueue LLM solution generation for a single question without changing the guide’s overall status. Use this when the auto-generated solution is incorrect and you want a fresh attempt rather than editing it manually. Returns 202 Accepted. POST /guides/:id/questions/:qid/regenerate-solution

Path parameters

id
string
required
Guide UUID.
qid
string
required
Question UUID.

Response 202

{ "enqueued": true }

POST /guides/:id/publish

Publish the guide: materialise one Exercise per approved question (that has a confirmed topic), create an Assignment of kind GUIDE, create an AssignmentTarget for every actively enrolled student, and flip the guide status to PUBLISHED. Students cannot see the guide until this endpoint is called. POST /guides/:id/publish
The guide must be in REVIEW status. All questions must be in either APPROVED or EXCLUDED status and at least one question must be APPROVED. Questions approved without a confirmed topic will still be approved but will not generate an Exercise — they are gradable but do not contribute to BKT mastery.

Path parameters

id
string
required
Guide UUID. Must currently be in REVIEW status.

Response 201

{
  "guide": { "id": "gd_abc123", "status": "PUBLISHED", "publishedAt": "2025-09-01T10:30:00.000Z" },
  "assignmentId": "asgn_xyz789",
  "materializedExercises": 7,
  "approvedWithoutTopic": 1,
  "studentsAssigned": 28
}
guide
object
The updated guide record in PUBLISHED status.
assignmentId
string
UUID of the newly created assignment.
materializedExercises
number
Number of Exercise records created.
approvedWithoutTopic
number
Number of approved questions that were skipped because they had no confirmed topic.
studentsAssigned
number
Number of students who received an AssignmentTarget.

Example

curl -X POST https://api.innova.app/guides/gd_abc123/publish \
  -H "Authorization: Bearer <token>"

DELETE /guides/:id

Soft-delete (archive) a guide. Sets status to ARCHIVED and records archivedAt. Archived guides are excluded from GET /guides results. This transition is irreversible — there is no unarchive endpoint. DELETE /guides/:id

Path parameters

id
string
required
Guide UUID.

Response 200

Returns the updated guide record with status: "ARCHIVED".

GET /guides/:id/source-url

Get a short-lived presigned S3 GET URL to view the original uploaded PDF in the browser. TTL defaults to 300 seconds (configurable via GUIDES_PRESIGNED_GET_TTL). GET /guides/:id/source-url

Path parameters

id
string
required
Guide UUID.

Response 200

{ "url": "https://s3.amazonaws.com/innova-guides/guides/uploads/uuid.pdf?X-Amz-..." }
url
string
Presigned S3 GET URL. Valid for the configured TTL (default 5 minutes). Do not cache — request a fresh URL for each viewer session.

GET /guides/:id/results

Results matrix for a published guide: latest submission per student × question combination, with per-question common-error aggregation. Use this to render the teacher’s C11 class results dashboard. GET /guides/:id/results

Path parameters

id
string
required
Guide UUID. The guide should be PUBLISHED for meaningful results.

Response 200

guideId
string
The guide UUID.
dueAt
string | null
ISO 8601 due date, or null if none was set.
students
StudentSummary[]
Active enrolled students, sorted by display name.
questions
QuestionSummary[]
Approved questions in sequence order.
cells
ResultCell[]
Flattened latest-submission-per-cell data. Join on questionId + studentId to build the matrix.
commonErrors
CommonErrorRow[]
Top-5 error tags per question across all incorrect submissions.

GET /guides/:id/submissions/:sid

Submission detail for the results drawer (C11). Returns presigned photo URLs, OCR transcription, alignment data against the canonical solution, and the effective error tag. GET /guides/:id/submissions/:sid

Path parameters

id
string
required
Guide UUID.
sid
string
required
Submission UUID.

Response 200

submissionId
string
Submission UUID.
questionSequence
number
1-based question position in the guide.
questionLabel
string
Question display label.
statementLatex
string
Question statement in LaTeX.
status
string
Submission pipeline status.
score
number | null
Numeric score.
isCorrect
boolean | null
Whether graded correct.
attemptNumber
number
1-based attempt count.
transcriptionLatex
string | null
Full OCR transcription in LaTeX.
transcriptionConfidence
number | null
OCR confidence.
alignmentJson
object | null
Step-level alignment of the student’s work against the canonical solution.
failureReason
string | null
Pipeline failure message when applicable.
photoUrls
string[]
Presigned S3 GET URLs for submission photos (TTL: default 300 seconds).
errorTagCode
string | null
Effective error tag code (teacher override preferred).
errorTagName
string | null
Effective error tag display name.
isOverridden
boolean
Whether the tag was set by a teacher override.

PATCH /guides/:id/submissions/:sid/error-tag

Override or clear a submission’s error tag manually. Stores the override on the submission row so it takes precedence over the pipeline-assigned tag everywhere (results matrix, mastery updates). Pass errorTagCode: null to clear a previous override and revert to the pipeline tag. PATCH /guides/:id/submissions/:sid/error-tag

Path parameters

id
string
required
Guide UUID.
sid
string
required
Submission UUID.

Request body

errorTagCode
string | null
required
Error tag code from the catalog (e.g. SUB_BORROW_NO_REGROUP), or null to clear the override and revert to the pipeline tag.

Response 200

{
  "submissionId": "sub_abc123",
  "errorTagCode": "SUB_BORROW_NO_REGROUP",
  "errorTagName": "Resta sin reagrupar",
  "isOverridden": true
}
submissionId
string
Submission UUID.
errorTagCode
string | null
Applied error tag code, or null after clearing.
errorTagName
string | null
Applied error tag display name.
isOverridden
boolean
true when an override is active; false after clearing.

Example

curl -X PATCH https://api.innova.app/guides/gd_abc123/submissions/sub_xyz/error-tag \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "errorTagCode": "SUB_BORROW_NO_REGROUP" }'

Guide state machine

A guide progresses through a defined set of statuses. Invalid transitions are rejected with 400 Bad Request by assertTransition (ADR-119).
UPLOADED ──────────────────────────────────────────────────────► ARCHIVED


EXTRACTING ──────────────────► EXTRACTION_FAILED ──────────────► ARCHIVED
   │                                    │ (retry via POST /ingest)
   ▼                                    ▼
GENERATING_SOLUTIONS ◄──────────────────┘

   ├──────────────────────────────────────────────────────────── ARCHIVED

   ├──► GENERATION_FAILED ──────────────────────────────────────► ARCHIVED
   │         │ (retry via POST /ingest)
   │         ├──► EXTRACTING
   │         └──► GENERATING_SOLUTIONS


  REVIEW ◄─────────────────────── (also from GENERATION_FAILED via retry)

   ├──► GENERATING_SOLUTIONS  (re-trigger solution generation without re-extracting)

   ├──────────────────────────────────────────────────────────── ARCHIVED


PUBLISHED


ARCHIVED
The full set of valid transitions is:
FromTo (allowed)
UPLOADEDEXTRACTING, ARCHIVED
EXTRACTINGGENERATING_SOLUTIONS, EXTRACTION_FAILED, ARCHIVED
EXTRACTION_FAILEDEXTRACTING, ARCHIVED
GENERATING_SOLUTIONSREVIEW, GENERATION_FAILED, ARCHIVED
GENERATION_FAILEDEXTRACTING, GENERATING_SOLUTIONS, ARCHIVED
REVIEWPUBLISHED, GENERATING_SOLUTIONS, ARCHIVED
PUBLISHEDARCHIVED
ARCHIVED(none)
StatusDescription
UPLOADEDGuide record created; PDF upload pending or complete.
EXTRACTINGPDF extraction worker is running.
EXTRACTION_FAILEDExtraction failed. Call POST /guides/:id/ingest to retry (moves back to EXTRACTING).
GENERATING_SOLUTIONSLLM solution-generation worker is running for extracted questions.
GENERATION_FAILEDSolution generation failed. Call POST /guides/:id/ingest to retry (moves to EXTRACTING or GENERATING_SOLUTIONS).
REVIEWExtraction and generation complete. Teacher reviews questions in the wizard. Can re-trigger solution generation without re-extracting.
PUBLISHEDGuide published; exercises and assignment materialised; visible to students.
ARCHIVEDGuide soft-deleted. Hidden from all listings.
Nothing is visible to students before PUBLISHED. Guides in REVIEW or earlier statuses are invisible to the student-facing app regardless of enrollment status.
Poll GET /guides/:id on a 3-second interval after POST /guides/:id/ingest to track progress through EXTRACTINGGENERATING_SOLUTIONSREVIEW. Typical end-to-end time for a 10-question worksheet is under 60 seconds.

Build docs developers (and LLMs) love