This document describes the backend HTTP surface mounted by the current Express application.
Base URL in local Docker development: http://localhost:4000
Primary router entrypoints:
- Validation failures generally return
400with anerrorfield and oftendetails - Auth failures are usually
401 - Ownership or existence failures are usually
404 - State conflicts are usually
409 - Unsupported preview or file types are usually
415 - Unhandled server failures return
500
- Auth: none
- Purpose: basic service metadata
- Response
200:
{ "service": "integricode-backend", "status": "running" }- Auth: none
- Purpose: compact health probe for API process and dependencies
- Response:
200forokordegraded,503forerror - Shape:
{
"status": "ok",
"checks": {
"database": { "status": "ok" },
"reports": { "status": "ok" },
"storage": { "status": "ok" }
}
}- Auth: none
- Purpose: detailed monitoring payload for diagnostics
- Response:
200forokordegraded,503forerror - Notes:
- includes database, report runner, and storage details
- includes summary fields for degradation reasons and recent report failures
Mounted at /api/auth
- Auth: none
- Purpose: check whether an auth email is available
- Request body:
{ "email": "jane@example.edu" }- Response
200:
{ "available": true }- Errors:
400invalid email address500failed lookup
- Auth: handled by Better Auth
- Purpose: session and auth endpoints delegated to
better-auth - Notes:
- mounted via
toNodeHandler(auth) - exact endpoint set depends on Better Auth configuration
- mounted via
Mounted at /api/v1/submissions
- Auth: none
- Purpose: expose the public RSA config used by submission clients
- Response
200:
{
"kid": "dev-kid-1",
"alg": "RSA-OAEP-256",
"publicKeyJwk": { "kty": "RSA", "n": "...", "e": "AQAB" },
"expiresAt": "2026-04-09T12:00:00.000Z"
}- Auth: none
- Purpose: return the active submission/privacy policy
- Errors:
503no active policy configured500failed policy lookup
- Auth: none
- Purpose: create a student submission
- Content type:
multipart/form-data - Required multipart fields:
assignmentKeysubmitterEncryptedsubmitterEncryptionfile
- Optional multipart fields:
submissionTextBase64
- Important behaviors:
- rate limited
- temporary abuse blocks can be applied after repeated invalid requests
- HTTPS is enforced in production
- file upload hard-limit is 5 MB at the route layer
- Success
201:
{
"submissionId": "uuid",
"submissionPublicKey": "uuid",
"assignmentId": "uuid",
"submittedAt": "2026-04-08T12:00:00.000Z",
"expiresAt": "2026-05-08T12:00:00.000Z"
}- Common errors:
400invalid multipart payload, invalid JSON fields, invalid encryption payload401policy acknowledgement required404assignment key invalid409policy version mismatch or resubmission blocked410assignment due window closed413file too large415unsupported file type or archive contents429rate limit or temporary abuse block503no active privacy policy configured
- Auth: none
- Purpose: confirm submission status using the public confirmation key
- Response
200:
{
"submission": {
"submissionId": "uuid",
"submissionPublicKey": "uuid",
"assignmentId": "uuid",
"submittedAt": "2026-04-08T12:00:00.000Z",
"expiresAt": "2026-05-08T12:00:00.000Z",
"status": "active"
}
}- Errors:
400invalid public key format404submission not found
Mounted at /api/v1/instructors
All routes in this section require a valid authenticated session via requireAuth.
- Purpose: fetch current instructor profile
- Response
200:
{
"instructor": {
"instructor_id": "uuid",
"name": "Jane Doe",
"email": "jane@example.edu",
"institution": "University of Example"
}
}- Errors:
404instructor record not found500failed lookup
- Purpose: update current instructor profile and sync the Better Auth user row
- Request body fields:
nameemailinstitution
- Notes:
- validates with Zod
- email conflicts return
409
- Errors:
400invalid body404instructor not found409email already in use500auth-user sync or update failure
- Purpose: fetch instructor notification preferences
- Purpose: update instructor notification preferences
- Errors:
400invalid body500update failure
- Purpose: send a test notification email
- Errors:
400invalid body or missing notification email503email service not configured500send failure
- Purpose: fetch instructor billing profile and related billing view data
- Purpose: create a Stripe billing portal session
- Errors:
404no billing customer found500portal session creation failed
Mounted at /api/v1/instructor
All routes in this section require a valid authenticated session via requireAuth.
- Purpose: create an assignment for the authenticated instructor
- Success
201: returns the assignment payload - Errors:
400invalid body409unique assignment-key generation failed500creation failure
- Purpose: list assignments owned by the authenticated instructor
- Purpose: fetch one instructor-owned assignment
- Errors:
400invalid assignment ID404assignment not found
- Purpose: delete an instructor-owned assignment
- Success
204 - Errors:
400invalid assignment ID404assignment not found
- Purpose: list submissions for an assignment
- Errors:
400invalid assignment ID or query404assignment not found
- Purpose: fetch submission detail for instructor review
- Errors:
400invalid IDs404submission not found
- Purpose: fetch previewable submission content
- Errors:
400invalid IDs404assignment or submission not found415preview unsupported for the stored file type
- Purpose: download the stored submission artifact
- Response: binary download
- Errors:
400invalid IDs404submission not found
- Purpose: list submission event history
- Errors:
400invalid IDs404submission not found
- Purpose: upload historical comparison data for an assignment
- Content type:
multipart/form-data - Success
201:
{ "upload": { "upload_id": "uuid" } }- Errors:
400invalid upload request404assignment not found413file exceeds configured max415unsupported file type or archive contents
- Purpose: list historical uploads for an assignment
- Purpose: download one historical upload artifact
- Response: binary download
- Purpose: delete one historical upload
- Success
204 - Errors:
400invalid IDs404upload not found409upload is referenced by an in-progress report
- Purpose: stream a ZIP export for an assignment
- Response:
application/zip - Errors:
400invalid assignment ID404assignment not found500export artifact missing or export failed
- Purpose: queue a report run for an assignment
- Success
202: returns the queued report payload - Notable request concepts:
- optional historical upload IDs
- optional exclusion upload IDs
- optional similarity threshold override
- Errors:
400invalid assignment/body, not enough eligible inputs, invalid uploads404assignment not found409report already in progress500required artifacts missing from storage
- Purpose: list reports for an assignment
- Purpose: fetch the latest report for an assignment
- Errors:
400invalid assignment ID404assignment or report not found
- Purpose: fetch one report by ID
- Notes:
- route-level response shape comes from report service output
- public-facing report status is normalized to
in-progress | complete | failed
- Purpose: preview a report input's source content
- Errors:
400invalid IDs404assignment, report input, submission, historical upload, or source text not found415preview unsupported500historical upload artifact missing
- Purpose: fetch detailed pair comparison data for one report pair
- Errors:
400invalid IDs404assignment, report, or pair not found415preview unavailable for the requested pair/input mix
Mounted at /api/v1/stripe
POST /webhookis intentionally unauthenticated- all other Stripe routes perform session lookup inside Stripe middleware
- most authenticated Stripe routes also require verified email
POST /create-checkout-sessionandPOST /verify-sessionallow unverified authenticated users
- Auth: none
- Purpose: list active billing plans
- Auth: authenticated session
- Purpose: fetch the current user's subscription and billing details
- Auth: authenticated session
- Purpose: create a hosted Stripe checkout session for
proorenterprise - Errors:
400invalid body, invalid plan, or user already has an active subscription500checkout creation failed
- Auth: authenticated session with verified email
- Purpose: switch an active subscription to another plan
- Errors:
400invalid body, invalid plan, or already on requested plan404no active subscription found500Stripe or local sync failure
- Auth: authenticated session with verified email
- Purpose: set
cancel_at_period_endon the current subscription - Errors:
404no active subscription found500cancellation failed
- Auth: authenticated session with verified email
- Purpose: remove scheduled cancellation for the current subscription
- Errors:
404no subscription found500reactivation failed
- Auth: authenticated session
- Purpose: verify a checkout session after redirect and return Stripe-side outcome details
- Errors:
400invalid body or payment not completed500verification failed
- Auth: none
- Purpose: receive Stripe webhook events and synchronize local billing state
- Body handling:
- must use the raw request body for signature verification
- signature comes from the
stripe-signatureheader
- Common outcomes:
checkout.session.completedupdates billing details and activates pending subscriptionsinvoice.payment_succeededcreates payment recordsinvoice.payment_failedmarks subscriptionspast_dueand records failed paymentscustomer.subscription.updatedsyncs local subscription statecustomer.subscription.deletedmarks subscriptions canceled
- Errors:
400signature verification failure or missing required metadata500webhook processing failure