Skip to main content

Version 1.0.3

Release Date: June 11, 2026

Anti-Cheat Flag System

A new proctoring-oriented flag system allows clients to report suspicious behavior during quiz attempts.

Submit Flags

POST /api/v1/attempts/{attempt_id}/flags
{
"labels": ["TAB_SWITCH", "CLIPBOARD"],
"detail": { "window_title": "Chrome - Google Search" },
"question_id": "550e8400-e29b-41d4-a716-446655440000"
}
  • Labels are free-form (max 50 chars), stored UPPERCASE.
  • The OXIDE_ prefix is reserved for system-generated flags (returns 400).
  • Limits: 20 labels/batch, 300 flags/attempt, 30s grace window after submit.

Flag Timeline & Summary

GET /api/v1/attempts/{attempt_id}/flags
GET /api/v1/quizzes/{quiz_id}/flags/summary

Owners can retrieve a chronological flag timeline per attempt and an aggregate summary (counts by label, top flagged participants) per quiz.

Attempt List Filter

GET /api/v1/info/attempts?isFlagged=true

New isFlagged query parameter filters the attempts list to only flagged entries.

Realtime attempt_flagged Event

Quiz owners subscribed to quiz:<id> receive live attempt_flagged events with labels[], participant_id, alias, and flagged_at. Detail payloads are REST-only.


Ticket History Query

The GET /api/v1/tickets endpoint now supports full history queries.

New Query Parameters

ParameterTypeDescription
include_inactiveboolWhen true, includes revoked, expired, and fully-consumed tickets. Default: false (usable only).
quiz_idnumberFilter tickets by quiz ID
event_idstringFilter tickets by event ID
pagenumberPage number (default: 1)
per_pagenumberItems per page (default: 100, max: 100)

Before (v1.0.2)

Only active/usable tickets returned — no way to see revoked or consumed history.

After (v1.0.3)

GET /api/v1/tickets?participant_id=...&include_inactive=true&page=1&per_page=20

Returns paginated results including expired, revoked, and fully-consumed tickets.


Attempt Transfer

New endpoint to transfer an attempt from one participant to another.

POST /api/v1/attempts/{attempt_id}/transfer

Useful when a participant was registered under the wrong alias or needs to be merged.


Question Template Improvements

Explanation Field

Question templates now accept an optional explanation field, matching the behavior of regular questions.

Custom Keys on Options

Template options now support arbitrary extra fields via serde(flatten):

{
"label": "A",
"value": "Paris",
"icon": "🇫🇷",
"color": "#0055A4"
}

Custom keys are preserved alongside label and value.


Search & Filter Fixes

  • Tag search: now matches questions containing any of the specified tags (OR logic) instead of requiring all tags (AND).
  • BM25 fuzzy search: fixed query generation to inline as SQL literal, resolving edge-case parse errors.
  • Event status filters: added upcoming and ended filters; status=available combined with from/to now returns VAL-001 validation error.
  • Event to filter: corrected to properly return only ended/past items when used alone.

Ticket Force-Submit Notifications

When a ticket is issued and the system auto-submits open attempts for that participant, connected clients now receive attempt_finished WebSocket events with reason: "forced_by_owner" (previously silent).


WebSocket Documentation

Full client-facing documentation for the WebSocket realtime API is now available at /docs/websocket, covering:

  • Connection authentication (?token= / ?session=)
  • Channel semantics (quiz:<id>, notification:<attempt_id>)
  • All event types with field-level payload tables
  • Heartbeat, reconnection guidance