openapi: 3.0.3
info:
title: 'Laravel API Documentation'
description: 'Mintfit V2 LMS - A modular monolith learning management system with microservice-style boundaries.'
version: 1.0.0
servers:
-
url: 'http://localhost'
tags:
-
name: 'Ad Management'
description: ''
-
name: 'Ad Tracking'
description: ''
-
name: 'Anonymous Sessions'
description: ''
-
name: 'Attempt Management'
description: ''
-
name: 'Block Management'
description: ''
-
name: 'Content Hierarchy'
description: ''
-
name: 'Content History'
description: ''
-
name: 'Content Management'
description: ''
-
name: 'Content Progress'
description: ''
-
name: 'Content Tagging'
description: ''
-
name: Endpoints
description: ''
-
name: Evaluation
description: ''
-
name: 'Global User Management'
description: ''
-
name: Health
description: ''
-
name: Identity
description: ''
-
name: 'Media Management'
description: ''
-
name: MoodleImport
description: ''
-
name: 'Notification Management'
description: ''
-
name: 'Notification Preferences'
description: ''
-
name: 'Pdf Management'
description: ''
-
name: 'Placement Management'
description: ''
-
name: Public
description: ''
-
name: 'Question History'
description: ''
-
name: 'Question Management'
description: ''
-
name: 'Question Tagging'
description: ''
-
name: 'RBAC Management'
description: ''
-
name: Statistics
description: ''
-
name: 'Tag Contexts'
description: ''
-
name: 'Tag Management'
description: ''
-
name: 'Tenant Management'
description: ''
-
name: 'Transfer - Import/Export'
description: ''
-
name: 'User Management'
description: ''
-
name: Verification
description: ''
paths:
/v1/ads:
post:
summary: 'Create a new ad'
operationId: createANewAd
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
'/v1/ads/{id}':
put:
summary: 'Update ad by ID'
operationId: updateAdByID
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
delete:
summary: 'Delete ad by ID (soft delete)'
operationId: deleteAdByIDsoftDelete
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the ad.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/ads/{id}/activate':
post:
summary: 'Activate an ad'
operationId: activateAnAd
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the ad.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/ads/{id}/pause':
post:
summary: 'Pause an ad'
operationId: pauseAnAd
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the ad.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/ads/{adId}/placements/{placementId}':
post:
summary: 'Assign ad to placement'
operationId: assignAdToPlacement
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
delete:
summary: 'Unassign ad from placement'
operationId: unassignAdFromPlacement
description: ''
parameters: []
responses: { }
tags:
- 'Ad Management'
security: []
parameters:
-
in: path
name: adId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
-
in: path
name: placementId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/ads/track/impression:
post:
summary: "Track an ad impression (public endpoint)\n\nThis endpoint is called when an ad is displayed to a user.\nThe tracking is processed asynchronously via a queued job."
operationId: trackAnAdImpressionpublicEndpointThisEndpointIsCalledWhenAnAdIsDisplayedToAUserTheTrackingIsProcessedAsynchronouslyViaAQueuedJob
description: ''
parameters: []
responses: { }
tags:
- 'Ad Tracking'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ad_id:
type: string
description: 'The id of an existing record in the ads table.'
example: architecto
placement_id:
type: string
description: 'This field is required when placement_key is not present. The id of an existing record in the placements table.'
example: architecto
nullable: true
placement_key:
type: string
description: 'This field is required when placement_id is not present.'
example: architecto
nullable: true
frontend:
type: string
description: 'This field is required when placement_key is present.'
example: architecto
nullable: true
tenant_id:
type: string
description: 'The id of an existing record in the tenants table.'
example: architecto
nullable: true
required:
- ad_id
security: []
/v1/ads/track/click:
post:
summary: "Track an ad click (public endpoint)\n\nThis endpoint is called when a user clicks on an ad.\nThe tracking is processed asynchronously via a queued job."
operationId: trackAnAdClickpublicEndpointThisEndpointIsCalledWhenAUserClicksOnAnAdTheTrackingIsProcessedAsynchronouslyViaAQueuedJob
description: ''
parameters: []
responses: { }
tags:
- 'Ad Tracking'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ad_id:
type: string
description: 'The id of an existing record in the ads table.'
example: architecto
placement_id:
type: string
description: 'This field is required when placement_key is not present. The id of an existing record in the placements table.'
example: architecto
nullable: true
placement_key:
type: string
description: 'This field is required when placement_id is not present.'
example: architecto
nullable: true
frontend:
type: string
description: 'This field is required when placement_key is present.'
example: architecto
nullable: true
tenant_id:
type: string
description: 'The id of an existing record in the tenants table.'
example: architecto
nullable: true
required:
- ad_id
security: []
/v1/anonymous-sessions:
get:
summary: "List anonymous sessions\n\nReturns a paginated list of anonymous sessions with optional filtering.\nRequires `anonymous_sessions.read` permission."
operationId: listAnonymousSessionsReturnsAPaginatedListOfAnonymousSessionsWithOptionalFilteringRequiresanonymousSessionsreadPermission
description: ''
parameters:
-
in: query
name: per_page
description: 'Items per page (1-100).'
example: 10
required: false
schema:
type: integer
description: 'Items per page (1-100).'
example: 10
-
in: query
name: page
description: 'Page number.'
example: 1
required: false
schema:
type: integer
description: 'Page number.'
example: 1
-
in: query
name: tenant_id
description: 'Filter by tenant ID.'
example: 01kav3fbpbpb3wnhtadsmrmt7m
required: false
schema:
type: string
description: 'Filter by tenant ID.'
example: 01kav3fbpbpb3wnhtadsmrmt7m
-
in: query
name: anonymous_id
description: 'Filter by anonymous ID.'
example: 01kav3fcmme10dwbeexyqkfndn
required: false
schema:
type: string
description: 'Filter by anonymous ID.'
example: 01kav3fcmme10dwbeexyqkfndn
-
in: query
name: search
description: 'Search in IP address and user agent.'
example: '192.168'
required: false
schema:
type: string
description: 'Search in IP address and user agent.'
example: '192.168'
-
in: query
name: logged_in_at_gte
description: 'date Filter sessions after this date.'
example: '2026-01-01'
required: false
schema:
type: string
description: 'date Filter sessions after this date.'
example: '2026-01-01'
-
in: query
name: logged_in_at_lte
description: 'date Filter sessions before this date.'
example: '2026-12-31'
required: false
schema:
type: string
description: 'date Filter sessions before this date.'
example: '2026-12-31'
-
in: query
name: is_upgraded
description: 'Filter by upgrade status.'
example: true
required: false
schema:
type: boolean
description: 'Filter by upgrade status.'
example: true
-
in: query
name: sort
description: 'Sort field (prefix with - for desc).'
example: '-logged_in_at'
required: false
schema:
type: string
description: 'Sort field (prefix with - for desc).'
example: '-logged_in_at'
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Anonymous sessions retrieved successfully'
data:
data: []
total: 0
per_page: 10
current_page: 1
last_page: 1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Anonymous sessions retrieved successfully'
data:
type: object
properties:
data:
type: array
example: []
total:
type: integer
example: 0
per_page:
type: integer
example: 10
current_page:
type: integer
example: 1
last_page:
type: integer
example: 1
tags:
- 'Anonymous Sessions'
'/v1/anonymous-sessions/{id}':
get:
summary: "Get anonymous session\n\nReturns a single anonymous session by ID.\nRequires `anonymous_sessions.read` permission."
operationId: getAnonymousSessionReturnsASingleAnonymousSessionByIDRequiresanonymousSessionsreadPermission
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Anonymous session retrieved successfully'
data:
id: 01kav3fcmme10dwbeexyqkfndn
tenant_id: 01kav3fbpbpb3wnhtadsmrmt7m
anonymous_id: 01kav3fcmme10dwbeexyqkfndn
ip_address: 127.0.0.1
user_agent: Mozilla/5.0
logged_in_at: '2026-03-04T00:00:00+00:00'
last_refreshed_at: null
refresh_count: 0
upgraded_to_identity_id: null
upgraded_at: null
expires_at: '2026-04-03T00:00:00+00:00'
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Anonymous session retrieved successfully'
data:
type: object
properties:
id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
tenant_id:
type: string
example: 01kav3fbpbpb3wnhtadsmrmt7m
anonymous_id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
ip_address:
type: string
example: 127.0.0.1
user_agent:
type: string
example: Mozilla/5.0
logged_in_at:
type: string
example: '2026-03-04T00:00:00+00:00'
last_refreshed_at:
type: string
example: null
refresh_count:
type: integer
example: 0
upgraded_to_identity_id:
type: string
example: null
upgraded_at:
type: string
example: null
expires_at:
type: string
example: '2026-04-03T00:00:00+00:00'
404:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Anonymous session not found'
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Anonymous session not found'
tags:
- 'Anonymous Sessions'
parameters:
-
in: path
name: id
description: 'The anonymous session ID.'
example: 01kav3fcmme10dwbeexyqkfndn
required: true
schema:
type: string
/v1/attempts/start:
post:
summary: 'Start a new attempt'
operationId: startANewAttempt
description: "Creates a new attempt for an attemptable entity (e.g., quiz_version).\nThe attempt is created in 'in_progress' state.\nThe identity_id is automatically set from the authenticated user."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Attempt started successfully'
data:
id: 01JGATTEMPT000000000001
identity_id: 01JGUSER00000000000001
attemptable_type: quiz_version
attemptable_id: 01JGQUIZVERSION0000001
status: in_progress
started_at: '2024-01-15T10:30:00Z'
submitted_at: null
abandoned_at: null
total_score: null
max_possible_score: null
metadata: { }
slot_history: []
responses: []
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Attempt started successfully'
data:
type: object
properties:
id:
type: string
example: 01JGATTEMPT000000000001
identity_id:
type: string
example: 01JGUSER00000000000001
attemptable_type:
type: string
example: quiz_version
attemptable_id:
type: string
example: 01JGQUIZVERSION0000001
status:
type: string
example: in_progress
started_at:
type: string
example: '2024-01-15T10:30:00Z'
submitted_at:
type: string
example: null
abandoned_at:
type: string
example: null
total_score:
type: string
example: null
max_possible_score:
type: string
example: null
metadata:
type: object
properties: { }
slot_history:
type: array
example: []
responses:
type: array
example: []
tags:
- 'Attempt Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
attemptable_type:
type: string
description: 'The type of attemptable entity.'
example: quiz_version
attemptable_id:
type: string
description: 'The attemptable entity ID (ULID).'
example: 01JGQUIZVERSION0000001
metadata:
type: object
description: 'Optional metadata for the attempt.'
example: []
properties: { }
required:
- attemptable_type
- attemptable_id
security: []
/v1/attempts/for-attemptable:
get:
summary: 'Get attempts for attemptable'
operationId: getAttemptsForAttemptable
description: "Retrieves paginated attempts for a specific identity and attemptable entity.\nUsers can only view their own attempts unless they have the 'attempts.read_all' permission."
parameters:
-
in: query
name: identity_id
description: 'The identity ID.'
example: 01JGUSER00000000000001
required: true
schema:
type: string
description: 'The identity ID.'
example: 01JGUSER00000000000001
-
in: query
name: attemptable_type
description: 'The attemptable type.'
example: quiz_version
required: true
schema:
type: string
description: 'The attemptable type.'
example: quiz_version
-
in: query
name: attemptable_id
description: 'The attemptable ID.'
example: 01JGQUIZVERSION0000001
required: true
schema:
type: string
description: 'The attemptable ID.'
example: 01JGQUIZVERSION0000001
-
in: query
name: per_page
description: 'Optional items per page.'
example: 10
required: false
schema:
type: integer
description: 'Optional items per page.'
example: 10
-
in: query
name: page
description: 'Optional page number.'
example: 1
required: false
schema:
type: integer
description: 'Optional page number.'
example: 1
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Attempts retrieved successfully'
data:
current_page: 1
data: []
total: 0
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Attempts retrieved successfully'
data:
type: object
properties:
current_page:
type: integer
example: 1
data:
type: array
example: []
total:
type: integer
example: 0
tags:
- 'Attempt Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
identity_id:
type: string
description: 'Must be 26 characters.'
example: bngzmiyvdljnikhwaykcmyuwpw
nullable: true
attemptable_type:
type: string
description: ''
example: architecto
attemptable_id:
type: string
description: 'Must be 26 characters.'
example: ngzmiyvdljnikhwaykcmyuwpwl
per_page:
type: integer
description: 'Must be at least 1. Must not be greater than 100.'
example: 20
nullable: true
page:
type: integer
description: 'Must be at least 1.'
example: 43
nullable: true
required:
- attemptable_type
- attemptable_id
security: []
/v1/attempts/record-answer:
post:
summary: 'Record an answer'
operationId: recordAnAnswer
description: "Records or updates a user's answer to a question.\nSupports optimistic locking via current_version parameter."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Answer recorded successfully'
data:
id: 01JGRESPONSE0000000001
attempt_id: 01JGATTEMPT000000000001
slot_option_id: 01JGQUESTION00000000001
answer_snapshot:
selected_option_id: b
submission_state: answered
points_awarded: null
is_correct: null
response_version: 1
first_responded_at: '2024-01-15T10:31:00Z'
last_responded_at: '2024-01-15T10:31:00Z'
time_spent_seconds: 45
metadata: null
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Answer recorded successfully'
data:
type: object
properties:
id:
type: string
example: 01JGRESPONSE0000000001
attempt_id:
type: string
example: 01JGATTEMPT000000000001
slot_option_id:
type: string
example: 01JGQUESTION00000000001
answer_snapshot:
type: object
properties:
selected_option_id:
type: string
example: b
submission_state:
type: string
example: answered
points_awarded:
type: string
example: null
is_correct:
type: string
example: null
response_version:
type: integer
example: 1
first_responded_at:
type: string
example: '2024-01-15T10:31:00Z'
last_responded_at:
type: string
example: '2024-01-15T10:31:00Z'
time_spent_seconds:
type: integer
example: 45
metadata:
type: string
example: null
tags:
- 'Attempt Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
attempt_id:
type: string
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
slot_option_id:
type: string
description: 'The slot option ID (ULID).'
example: 01JGQUESTION00000000001
answer_data:
type: object
description: 'The answer data.'
example:
selected_option_id: b
properties: { }
time_spent_seconds:
type: integer
description: 'Optional time spent on question.'
example: 16
current_version:
type: integer
description: 'Optional current response version for optimistic locking.'
example: 16
metadata:
type: object
description: 'Optional response metadata.'
example: []
properties: { }
required:
- attempt_id
- slot_option_id
- answer_data
security: []
/v1/attempts/record-slot-display:
post:
summary: 'Record slot display'
operationId: recordSlotDisplay
description: "Records that a slot/question was displayed to the user.\nUsed for navigation tracking and scheduler context."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Slot display recorded successfully'
data:
id: 01JGHISTORY00000000001
attempt_id: 01JGATTEMPT000000000001
slot_id: 01JGSLOT000000000000001
slot_option_id: 01JGQUESTION00000000001
displayed_at: '2024-01-15T10:30:30Z'
display_sequence: 1
navigation_method: sequential
metadata: null
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Slot display recorded successfully'
data:
type: object
properties:
id:
type: string
example: 01JGHISTORY00000000001
attempt_id:
type: string
example: 01JGATTEMPT000000000001
slot_id:
type: string
example: 01JGSLOT000000000000001
slot_option_id:
type: string
example: 01JGQUESTION00000000001
displayed_at:
type: string
example: '2024-01-15T10:30:30Z'
display_sequence:
type: integer
example: 1
navigation_method:
type: string
example: sequential
metadata:
type: string
example: null
tags:
- 'Attempt Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
attempt_id:
type: string
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
slot_id:
type: string
description: 'The slot ID (ULID).'
example: 01JGSLOT000000000000001
slot_option_id:
type: string
description: 'Optional slot option ID if slot contains a question.'
example: architecto
navigation_method:
type: string
description: 'Optional navigation method.'
example: sequential
metadata:
type: object
description: 'Optional display metadata.'
example: []
properties: { }
required:
- attempt_id
- slot_id
security: []
'/v1/attempts/{id}':
get:
summary: 'Get attempt by ID'
operationId: getAttemptByID
description: 'Retrieves complete attempt details including all responses and slot history.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Attempt retrieved successfully'
data:
id: 01JGATTEMPT000000000001
identity_id: 01JGUSER00000000000001
attemptable_type: quiz_version
attemptable_id: 01JGQUIZVERSION0000001
status: in_progress
started_at: '2024-01-15T10:30:00Z'
submitted_at: null
abandoned_at: null
total_score: null
max_possible_score: null
metadata: { }
slot_history: []
responses: []
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Attempt retrieved successfully'
data:
type: object
properties:
id:
type: string
example: 01JGATTEMPT000000000001
identity_id:
type: string
example: 01JGUSER00000000000001
attemptable_type:
type: string
example: quiz_version
attemptable_id:
type: string
example: 01JGQUIZVERSION0000001
status:
type: string
example: in_progress
started_at:
type: string
example: '2024-01-15T10:30:00Z'
submitted_at:
type: string
example: null
abandoned_at:
type: string
example: null
total_score:
type: string
example: null
max_possible_score:
type: string
example: null
metadata:
type: object
properties: { }
slot_history:
type: array
example: []
responses:
type: array
example: []
tags:
- 'Attempt Management'
security: []
delete:
summary: ''
operationId: deleteV1AttemptsId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
required: true
schema:
type: string
'/v1/attempts/{id}/submit':
post:
summary: 'Submit attempt'
operationId: submitAttempt
description: 'Submits the attempt for grading. Makes the attempt immutable.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Attempt submitted successfully'
data:
id: 01JGATTEMPT000000000001
status: submitted
submitted_at: '2024-01-15T10:45:00Z'
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Attempt submitted successfully'
data:
type: object
properties:
id:
type: string
example: 01JGATTEMPT000000000001
status:
type: string
example: submitted
submitted_at:
type: string
example: '2024-01-15T10:45:00Z'
tags:
- 'Attempt Management'
security: []
parameters:
-
in: path
name: id
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
required: true
schema:
type: string
'/v1/attempts/{id}/abandon':
post:
summary: 'Abandon attempt'
operationId: abandonAttempt
description: 'Abandons the attempt. Makes the attempt immutable.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Attempt abandoned successfully'
data:
id: 01JGATTEMPT000000000001
status: abandoned
abandoned_at: '2024-01-15T10:45:00Z'
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Attempt abandoned successfully'
data:
type: object
properties:
id:
type: string
example: 01JGATTEMPT000000000001
status:
type: string
example: abandoned
abandoned_at:
type: string
example: '2024-01-15T10:45:00Z'
tags:
- 'Attempt Management'
security: []
parameters:
-
in: path
name: id
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
required: true
schema:
type: string
'/v1/attempts/{id}/scheduler-context':
get:
summary: 'Get scheduler runtime context'
operationId: getSchedulerRuntimeContext
description: 'Returns context data needed by Quiz scheduler drivers.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Scheduler context retrieved successfully'
data:
used_question_ids:
- 01JGQUESTION00000000001
current_score: 0
max_score: 0
answered_count: 1
skipped_count: 0
elapsed_time_seconds: 120
metadata: { }
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Scheduler context retrieved successfully'
data:
type: object
properties:
used_question_ids:
type: array
example:
- 01JGQUESTION00000000001
items:
type: string
current_score:
type: integer
example: 0
max_score:
type: integer
example: 0
answered_count:
type: integer
example: 1
skipped_count:
type: integer
example: 0
elapsed_time_seconds:
type: integer
example: 120
metadata:
type: object
properties: { }
tags:
- 'Attempt Management'
security: []
parameters:
-
in: path
name: id
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
required: true
schema:
type: string
'/v1/attempts/{attemptId}/skip/{slotOptionId}':
post:
summary: 'Skip a question'
operationId: skipAQuestion
description: 'Marks a question as explicitly skipped.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Question skipped successfully'
data:
id: 01JGRESPONSE0000000001
attempt_id: 01JGATTEMPT000000000001
slot_option_id: 01JGQUESTION00000000001
answer_snapshot: { }
submission_state: skipped
points_awarded: null
is_correct: null
response_version: 1
first_responded_at: '2024-01-15T10:31:00Z'
last_responded_at: '2024-01-15T10:31:00Z'
time_spent_seconds: null
metadata: null
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Question skipped successfully'
data:
type: object
properties:
id:
type: string
example: 01JGRESPONSE0000000001
attempt_id:
type: string
example: 01JGATTEMPT000000000001
slot_option_id:
type: string
example: 01JGQUESTION00000000001
answer_snapshot:
type: object
properties: { }
submission_state:
type: string
example: skipped
points_awarded:
type: string
example: null
is_correct:
type: string
example: null
response_version:
type: integer
example: 1
first_responded_at:
type: string
example: '2024-01-15T10:31:00Z'
last_responded_at:
type: string
example: '2024-01-15T10:31:00Z'
time_spent_seconds:
type: string
example: null
metadata:
type: string
example: null
tags:
- 'Attempt Management'
security: []
parameters:
-
in: path
name: attemptId
description: 'The attempt ID (ULID).'
example: 01JGATTEMPT000000000001
required: true
schema:
type: string
-
in: path
name: slotOptionId
description: 'The slot option ID (ULID).'
example: 01JGQUESTION00000000001
required: true
schema:
type: string
/v1/blocks/schemas:
get:
summary: "Get all block schemas\n\nReturns JSON schemas for all supported block types.\nSchemas define the structure and validation rules for each block type.\nPublic endpoint - no authentication required."
operationId: getAllBlockSchemasReturnsJSONSchemasForAllSupportedBlockTypesSchemasDefineTheStructureAndValidationRulesForEachBlockTypePublicEndpointNoAuthenticationRequired
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Block schemas retrieved successfully'
data:
-
type: paragraph
schema:
type: object
properties:
content:
type: string
-
type: headline
schema:
type: object
properties:
content:
type: string
level:
type: integer
minimum: 1
maximum: 6
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Block schemas retrieved successfully'
data:
type: array
example:
-
type: paragraph
schema:
type: object
properties:
content:
type: string
-
type: headline
schema:
type: object
properties:
content:
type: string
level:
type: integer
minimum: 1
maximum: 6
items:
type: object
properties:
type:
type: string
example: paragraph
schema:
type: object
properties:
type:
type: string
example: object
properties:
type: object
properties:
content:
type: object
properties:
type:
type: string
example: string
tags:
- 'Block Management'
security: []
'/v1/blocks/schemas/{type}':
get:
summary: "Get schema for specific block type\n\nReturns the JSON schema for a specific block type.\nSupported types: paragraph, headline, image, table.\nPublic endpoint - no authentication required."
operationId: getSchemaForSpecificBlockTypeReturnsTheJSONSchemaForASpecificBlockTypeSupportedTypesParagraphHeadlineImageTablePublicEndpointNoAuthenticationRequired
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Schema retrieved successfully for type: paragraph'
data:
type: paragraph
schema:
type: object
properties:
content:
type: string
required:
- content
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Schema retrieved successfully for type: paragraph'
data:
type: object
properties:
type:
type: string
example: paragraph
schema:
type: object
properties:
type:
type: string
example: object
properties:
type: object
properties:
content:
type: object
properties:
type:
type: string
example: string
required:
type: array
example:
- content
items:
type: string
404:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Block type not found: unknown'
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Block type not found: unknown'
tags:
- 'Block Management'
security: []
parameters:
-
in: path
name: type
description: 'Block type.'
example: paragraph
required: true
schema:
type: string
/v1/blocks/validate:
post:
summary: "Validate array of blocks\n\nValidates an array of blocks against their JSON schemas.\nOptionally restrict validation to specific block types.\nReturns validation result with detailed error messages if validation fails.\nPublic endpoint - no authentication required."
operationId: validateArrayOfBlocksValidatesAnArrayOfBlocksAgainstTheirJSONSchemasOptionallyRestrictValidationToSpecificBlockTypesReturnsValidationResultWithDetailedErrorMessagesIfValidationFailsPublicEndpointNoAuthenticationRequired
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'All blocks are valid'
data:
is_valid: true
errors: []
properties:
success:
type: boolean
example: true
message:
type: string
example: 'All blocks are valid'
data:
type: object
properties:
is_valid:
type: boolean
example: true
errors:
type: array
example: []
400:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Validation failed'
errors:
is_valid: false
errors:
-
block_index: 0
message: "Block type 'unknown' is not supported"
-
block_index: 1
field: content
message: 'The content field is required'
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Validation failed'
errors:
type: object
properties:
is_valid:
type: boolean
example: false
errors:
type: array
example:
-
block_index: 0
message: "Block type 'unknown' is not supported"
-
block_index: 1
field: content
message: 'The content field is required'
items:
type: object
properties:
block_index:
type: integer
example: 0
message:
type: string
example: "Block type 'unknown' is not supported"
tags:
- 'Block Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
blocks:
type: array
description: 'Array of block objects to validate.'
example:
-
type: paragraph
content: Hello
items:
type: string
allowed_types:
type: array
description: 'Optional array of allowed block types. If provided, blocks with other types will fail validation.'
example:
- paragraph
- headline
items:
type: string
required:
- blocks
security: []
/v1/blocks/transform/html:
post:
summary: "Transform blocks to HTML\n\nTransforms an array of structured blocks into HTML markup.\nSupports paragraph, headline, image, and table block types.\nPreserves block structure and formatting.\nPublic endpoint - no authentication required."
operationId: transformBlocksToHTMLTransformsAnArrayOfStructuredBlocksIntoHTMLMarkupSupportsParagraphHeadlineImageAndTableBlockTypesPreservesBlockStructureAndFormattingPublicEndpointNoAuthenticationRequired
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Blocks transformed to HTML successfully'
data:
html: '
Hello world
' properties: success: type: boolean example: true message: type: string example: 'Blocks transformed to HTML successfully' data: type: object properties: html: type: string example: 'Hello world
' 400: description: '' content: application/json: schema: type: object example: success: false message: 'No blocks provided' properties: success: type: boolean example: false message: type: string example: 'No blocks provided' tags: - 'Block Management' requestBody: required: true content: application/json: schema: type: object properties: blocks: type: array description: 'Array of block objects to transform.' example: - type: paragraph content: 'Hello world' items: type: string language: type: string description: 'Optional language code for HTML lang attribute.' example: en required: - blocks security: [] /v1/blocks/transform/markdown: post: summary: "Transform blocks to Markdown\n\nTransforms an array of structured blocks into Markdown format.\nSupports paragraph, headline, image, and table block types.\nPreserves block structure and converts formatting to Markdown syntax.\nPublic endpoint - no authentication required." operationId: transformBlocksToMarkdownTransformsAnArrayOfStructuredBlocksIntoMarkdownFormatSupportsParagraphHeadlineImageAndTableBlockTypesPreservesBlockStructureAndConvertsFormattingToMarkdownSyntaxPublicEndpointNoAuthenticationRequired description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Blocks transformed to Markdown successfully' data: markdown: "# Title\n\nParagraph text here." properties: success: type: boolean example: true message: type: string example: 'Blocks transformed to Markdown successfully' data: type: object properties: markdown: type: string example: "# Title\n\nParagraph text here." 400: description: '' content: application/json: schema: type: object example: success: false message: 'No blocks provided' properties: success: type: boolean example: false message: type: string example: 'No blocks provided' tags: - 'Block Management' requestBody: required: true content: application/json: schema: type: object properties: blocks: type: array description: 'Array of block objects to transform.' example: - type: headline content: Title level: 1 items: type: string language: type: string description: 'Optional language code for metadata.' example: en required: - blocks security: [] /v1/blocks/transform/plaintext: post: summary: "Transform blocks to plain text\n\nTransforms an array of structured blocks into plain text without formatting.\nSupports paragraph, headline, image, and table block types.\nStrips all formatting and markup, preserving only text content.\nPublic endpoint - no authentication required." operationId: transformBlocksToPlainTextTransformsAnArrayOfStructuredBlocksIntoPlainTextWithoutFormattingSupportsParagraphHeadlineImageAndTableBlockTypesStripsAllFormattingAndMarkupPreservingOnlyTextContentPublicEndpointNoAuthenticationRequired description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Blocks transformed to plain text successfully' data: text: 'Hello world' properties: success: type: boolean example: true message: type: string example: 'Blocks transformed to plain text successfully' data: type: object properties: text: type: string example: 'Hello world' 400: description: '' content: application/json: schema: type: object example: success: false message: 'No blocks provided' properties: success: type: boolean example: false message: type: string example: 'No blocks provided' tags: - 'Block Management' requestBody: required: true content: application/json: schema: type: object properties: blocks: type: array description: 'Array of block objects to transform.' example: - type: paragraph content: Hello items: type: string language: type: string description: 'Optional language code for metadata.' example: en required: - blocks security: [] /v1/blocks/parse/html: post: summary: "Parse HTML to blocks\n\nParses HTML content into structured block objects.\nConverts common HTML tags (p, h1-h6, img, table) into corresponding block types.\nReturns an array of validated block objects.\nPublic endpoint - no authentication required." operationId: parseHTMLToBlocksParsesHTMLContentIntoStructuredBlockObjectsConvertsCommonHTMLTagspH1H6ImgTableIntoCorrespondingBlockTypesReturnsAnArrayOfValidatedBlockObjectsPublicEndpointNoAuthenticationRequired description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'HTML parsed to blocks successfully' data: blocks: - type: paragraph content: 'Hello world' - type: headline content: Title level: 1 properties: success: type: boolean example: true message: type: string example: 'HTML parsed to blocks successfully' data: type: object properties: blocks: type: array example: - type: paragraph content: 'Hello world' - type: headline content: Title level: 1 items: type: object properties: type: type: string example: paragraph content: type: string example: 'Hello world' 400: description: '' content: application/json: schema: type: object example: success: false message: 'No HTML content provided' properties: success: type: boolean example: false message: type: string example: 'No HTML content provided' tags: - 'Block Management' requestBody: required: true content: application/json: schema: type: object properties: html: type: string description: 'HTML content to parse.' example: 'Hello world
id of an existing record in the contents table.'
example:
- architecto
items:
type: string
required:
- ordered_ids
security: []
parameters:
-
in: path
name: parentId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/history/compare':
post:
summary: 'Compare two versions'
operationId: compareTwoVersions
description: ''
parameters: []
responses: { }
tags:
- 'Content History'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
version1:
type: string
description: ''
example: architecto
version2:
type: string
description: ''
example: architecto
required:
- version1
- version2
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/history/{historyId}/restore':
post:
summary: 'Restore a previous version'
operationId: restoreAPreviousVersion
description: ''
parameters: []
responses: { }
tags:
- 'Content History'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
-
in: path
name: historyId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/content:
post:
summary: 'Create new content page'
operationId: createNewContentPage
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
'/v1/content/{id}':
put:
summary: 'Update content by ID'
operationId: updateContentByID
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
delete:
summary: 'Delete content by ID (soft delete)'
operationId: deleteContentByIDsoftDelete
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/slug/{slug}':
put:
summary: 'Update content by slug'
operationId: updateContentBySlug
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
delete:
summary: 'Delete content by slug (soft delete)'
operationId: deleteContentBySlugsoftDelete
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
parameters:
-
in: path
name: slug
description: 'The slug of the slug.'
example: architecto
required: true
schema:
type: string
'/v1/content/{id}/publish':
post:
summary: 'Publish content page'
operationId: publishContentPage
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/unpublish':
post:
summary: 'Unpublish content page (revert to draft)'
operationId: unpublishContentPagerevertToDraft
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/schedule':
post:
summary: 'Schedule content for future publication'
operationId: scheduleContentForFuturePublication
description: ''
parameters: []
responses: { }
tags:
- 'Content Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
published_at:
type: string
description: 'Must be a valid date. Must be a date after now.'
example: '2052-05-28'
required:
- published_at
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/progress/complete':
post:
summary: 'Mark content page as completed'
operationId: markContentPageAsCompleted
description: ''
parameters: []
responses: { }
tags:
- 'Content Progress'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/progress':
post:
summary: 'Update progress percentage for a content page'
operationId: updateProgressPercentageForAContentPage
description: ''
parameters: []
responses: { }
tags:
- 'Content Progress'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
progress_percentage:
type: integer
description: 'Must be at least 0. Must not be greater than 100.'
example: 1
required:
- progress_percentage
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/content/{id}/tags':
post:
summary: 'Assign tag(s) to content'
operationId: assignTagsToContent
description: ''
parameters: []
responses: { }
tags:
- 'Content Tagging'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
tag_slugs:
type: array
description: ''
example:
- architecto
items:
type: string
context:
type: string
description: 'Must not be greater than 50 characters.'
example: 'n'
nullable: true
required:
- tag_slugs
security: []
delete:
summary: 'Remove tag(s) from content'
operationId: removeTagsFromContent
description: ''
parameters: []
responses: { }
tags:
- 'Content Tagging'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
tag_slugs:
type: array
description: ''
example:
- architecto
items:
type: string
context:
type: string
description: 'Must not be greater than 50 characters.'
example: 'n'
nullable: true
required:
- tag_slugs
security: []
parameters:
-
in: path
name: id
description: 'The ID of the content.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/content/tags/bulk-assign:
post:
summary: 'Bulk assign tags to multiple contents'
operationId: bulkAssignTagsToMultipleContents
description: ''
parameters: []
responses: { }
tags:
- 'Content Tagging'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
assignments:
type: array
description: 'Must have at least 1 items.'
example:
- []
items:
type: object
properties:
resource_id:
type: string
description: 'Must be 26 characters.'
example: ngzmiyvdljnikhwaykcmyuwpwl
tag_slugs:
type: array
description: ''
example:
- architecto
items:
type: string
required:
- resource_id
- tag_slugs
context:
type: string
description: 'Must not be greater than 50 characters.'
example: b
nullable: true
required:
- assignments
security: []
/livewire/update:
post:
summary: ''
operationId: postLivewireUpdate
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
/livewire/upload-file:
post:
summary: ''
operationId: postLivewireUploadFile
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
'/v1/ads/{id}/restore/{historyId}':
post:
summary: ''
operationId: postV1AdsIdRestoreHistoryId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the ad.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
-
in: path
name: historyId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/certificate-definitions:
post:
summary: ''
operationId: postV1CertificateDefinitions
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
'/v1/certificate-definitions/{id}':
put:
summary: ''
operationId: putV1CertificateDefinitionsId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1CertificateDefinitionsId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the certificate definition.'
example: architecto
required: true
schema:
type: string
'/v1/certificates/enrollments/{enrollmentId}/issue':
post:
summary: 'Issue a participation certificate for an enrollment.'
operationId: issueAParticipationCertificateForAnEnrollment
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
signed_by:
type: string
description: 'Must be 26 characters.'
example: bngzmiyvdljnikhwaykcmyuwpw
nullable: true
signed_by_name:
type: string
description: 'Must not be greater than 255 characters.'
example: l
nullable: true
metadata:
type: object
description: ''
example: null
properties: { }
nullable: true
security: []
parameters:
-
in: path
name: enrollmentId
description: ''
example: architecto
required: true
schema:
type: string
/v1/certificates/issue:
post:
summary: 'Manually issue a certificate (admin)'
operationId: manuallyIssueACertificateadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
'/v1/certificates/{id}/revoke':
post:
summary: 'Revoke a certificate (admin)'
operationId: revokeACertificateadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
reason:
type: string
description: 'Must not be greater than 500 characters.'
example: b
required:
- reason
security: []
parameters:
-
in: path
name: id
description: 'The ID of the certificate.'
example: architecto
required: true
schema:
type: string
'/v1/certificates/{id}/regenerate-pdf':
post:
summary: 'Regenerate certificate PDF (admin)'
operationId: regenerateCertificatePDFadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the certificate.'
example: architecto
required: true
schema:
type: string
'/v1/certificates/{id}/recertify':
post:
summary: 'Re-certify an expired certificate (admin)'
operationId: reCertifyAnExpiredCertificateadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
metadata:
type: object
description: ''
example: null
properties: { }
nullable: true
security: []
parameters:
-
in: path
name: id
description: 'The ID of the certificate.'
example: architecto
required: true
schema:
type: string
/v1/courses:
post:
summary: 'Create a new course'
operationId: createANewCourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
'/v1/courses/{id}':
put:
summary: 'Update a course'
operationId: updateACourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: 'Delete a course'
operationId: deleteACourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the course.'
example: architecto
required: true
schema:
type: string
'/v1/courses/{id}/publish':
post:
summary: 'Publish a course'
operationId: publishACourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the course.'
example: architecto
required: true
schema:
type: string
'/v1/courses/{id}/archive':
post:
summary: 'Archive a course'
operationId: archiveACourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the course.'
example: architecto
required: true
schema:
type: string
'/v1/courses/{id}/activities':
post:
summary: 'Add activity to course'
operationId: addActivityToCourse
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
activity_type:
type: string
description: ''
example: quiz
enum:
- content
- quiz
activity_id:
type: string
description: 'Must be 26 characters.'
example: bngzmiyvdljnikhwaykcmyuwpw
title:
type: object
description: ''
example: null
properties: { }
nullable: true
description:
type: object
description: ''
example: null
properties: { }
nullable: true
sort_order:
type: integer
description: 'Must be at least 0.'
example: 89
nullable: true
is_required:
type: boolean
description: ''
example: false
nullable: true
is_graded:
type: boolean
description: ''
example: false
nullable: true
weight:
type: number
description: 'Must be at least 0.'
example: 34
nullable: true
unlock_rules:
type: object
description: ''
example: null
properties: { }
nullable: true
required:
- activity_type
- activity_id
security: []
parameters:
-
in: path
name: id
description: 'The ID of the course.'
example: architecto
required: true
schema:
type: string
'/v1/courses/activities/{activityId}':
put:
summary: 'Update activity'
operationId: updateActivity
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
title:
type: object
description: ''
example: null
properties: { }
nullable: true
description:
type: object
description: ''
example: null
properties: { }
nullable: true
sort_order:
type: integer
description: 'Must be at least 0.'
example: 27
nullable: true
is_required:
type: boolean
description: ''
example: false
nullable: true
is_graded:
type: boolean
description: ''
example: false
nullable: true
weight:
type: number
description: 'Must be at least 0.'
example: 39
nullable: true
unlock_rules:
type: object
description: ''
example: null
properties: { }
nullable: true
security: []
delete:
summary: 'Delete activity'
operationId: deleteActivity
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: activityId
description: ''
example: architecto
required: true
schema:
type: string
'/v1/courses/{id}/activities/reorder':
post:
summary: 'Reorder activities'
operationId: reorderActivities
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
activity_ids:
type: array
description: 'Must be 26 characters.'
example:
- bngzmiyvdljnikhwaykcmyuwpw
items:
type: string
required:
- activity_ids
security: []
parameters:
-
in: path
name: id
description: 'The ID of the course.'
example: architecto
required: true
schema:
type: string
'/v1/enrollments/{enrollmentId}/activities/{activityId}/start':
post:
summary: 'Start an activity'
operationId: startAnActivity
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: enrollmentId
description: ''
example: architecto
required: true
schema:
type: string
-
in: path
name: activityId
description: ''
example: architecto
required: true
schema:
type: string
'/v1/enrollments/{enrollmentId}/activities/{activityId}/complete':
post:
summary: 'Complete an activity'
operationId: completeAnActivity
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
score:
type: number
description: 'Must be at least 0. Must not be greater than 100.'
example: 1
nullable: true
security: []
parameters:
-
in: path
name: enrollmentId
description: ''
example: architecto
required: true
schema:
type: string
-
in: path
name: activityId
description: ''
example: architecto
required: true
schema:
type: string
'/v1/enrollments/{id}/suspend':
post:
summary: 'Suspend enrollment (admin)'
operationId: suspendEnrollmentadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the enrollment.'
example: architecto
required: true
schema:
type: string
'/v1/enrollments/{id}/reactivate':
post:
summary: 'Reactivate enrollment (admin)'
operationId: reactivateEnrollmentadmin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the enrollment.'
example: architecto
required: true
schema:
type: string
/dashboard/auth:
post:
summary: 'Handle password authentication.'
operationId: handlePasswordAuthentication
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
password:
type: string
description: ''
example: '|]|{+-'
required:
- password
security: []
/dashboard/logout:
post:
summary: 'Logout from dashboard.'
operationId: logoutFromDashboard
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
/v1/quizzes:
post:
summary: ''
operationId: postV1Quizzes
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
'/v1/quizzes/{id}':
put:
summary: ''
operationId: putV1QuizzesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1QuizzesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: id
description: 'The ID of the quiz.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/{slug}':
put:
summary: ''
operationId: putV1QuizzesSlug
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1QuizzesSlug
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: slug
description: 'The slug of the quiz.'
example: architecto
required: true
schema:
type: string
'/v1/quizzes/{quiz_id}/versions':
post:
summary: ''
operationId: postV1QuizzesQuiz_idVersions
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: quiz_id
description: 'The ID of the quiz.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/versions/{version_id}/publish':
post:
summary: ''
operationId: postV1QuizzesVersionsVersion_idPublish
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: version_id
description: 'The ID of the version.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/versions/{version_id}/duplicate':
post:
summary: ''
operationId: postV1QuizzesVersionsVersion_idDuplicate
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: version_id
description: 'The ID of the version.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/versions/{version_id}':
delete:
summary: ''
operationId: deleteV1QuizzesVersionsVersion_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: version_id
description: 'The ID of the version.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/quizzes/versions/compare:
post:
summary: 'Compare two quiz versions.'
operationId: compareTwoQuizVersions
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
old_version_id:
type: string
description: ''
example: architecto
new_version_id:
type: string
description: ''
example: architecto
required:
- old_version_id
- new_version_id
security: []
'/v1/quizzes/versions/{version_id}/sections':
post:
summary: ''
operationId: postV1QuizzesVersionsVersion_idSections
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: version_id
description: 'The ID of the version.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/sections/{section_id}':
put:
summary: ''
operationId: putV1QuizzesSectionsSection_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1QuizzesSectionsSection_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: section_id
description: 'The ID of the section.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/versions/{version_id}/sections/reorder':
post:
summary: ''
operationId: postV1QuizzesVersionsVersion_idSectionsReorder
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
section_ids:
type: array
description: 'Must be 26 characters.'
example:
- bngzmiyvdljnikhwaykcmyuwpw
items:
type: string
required:
- section_ids
security: []
parameters:
-
in: path
name: version_id
description: 'The ID of the version.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/sections/{section_id}/slots':
post:
summary: ''
operationId: postV1QuizzesSectionsSection_idSlots
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: section_id
description: 'The ID of the section.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/slots/{slot_id}':
put:
summary: ''
operationId: putV1QuizzesSlotsSlot_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1QuizzesSlotsSlot_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: slot_id
description: 'The ID of the slot.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/sections/{section_id}/slots/reorder':
post:
summary: ''
operationId: postV1QuizzesSectionsSection_idSlotsReorder
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
slot_ids:
type: array
description: 'Must be 26 characters.'
example:
- bngzmiyvdljnikhwaykcmyuwpw
items:
type: string
required:
- slot_ids
security: []
parameters:
-
in: path
name: section_id
description: 'The ID of the section.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/slots/{slot_id}/slot-options/bulk':
post:
summary: 'Bulk create slot options for a slot.'
operationId: bulkCreateSlotOptionsForASlot
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
options:
type: array
description: 'Must have at least 1 items.'
example:
- []
items:
type: object
properties:
original_question_id:
type: string
description: 'Must be at least 26 characters. Must not be greater than 26 characters.'
example: b
points:
type: number
description: 'Must be at least 0.'
example: 39
nullable: true
metadata:
type: object
description: ''
example: null
properties: { }
nullable: true
required:
- original_question_id
required:
- options
security: []
parameters:
-
in: path
name: slot_id
description: 'The ID of the slot.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/slots/{slot_id}/slot-options':
post:
summary: ''
operationId: postV1QuizzesSlotsSlot_idSlotOptions
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: slot_id
description: 'The ID of the slot.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/slot-options/{option_id}':
put:
summary: ''
operationId: putV1QuizzesSlotOptionsOption_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
delete:
summary: ''
operationId: deleteV1QuizzesSlotOptionsOption_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: option_id
description: 'The ID of the option.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/quizzes/slot-options/{option_id}/refresh':
post:
summary: ''
operationId: postV1QuizzesSlotOptionsOption_idRefresh
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
parameters:
-
in: path
name: option_id
description: 'The ID of the option.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/evaluation/manual-grading/{assignmentId}/grade':
post:
summary: 'Submit manual grade for assignment.'
operationId: submitManualGradeForAssignment
description: ''
parameters: []
responses: { }
tags:
- Evaluation
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
points_awarded:
type: number
description: 'Must be at least 0.'
example: 27
feedback:
type: string
description: 'Must not be greater than 10000 characters.'
example: 'n'
rubric_scores:
type: array
description: 'Must be at least 0.'
example:
- 84
items:
type: number
required:
- points_awarded
parameters:
-
in: path
name: assignmentId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/evaluation/manual-grading/{assignmentId}/assign':
put:
summary: 'Assign grading assignment to instructor.'
operationId: assignGradingAssignmentToInstructor
description: ''
parameters: []
responses: { }
tags:
- Evaluation
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
assigned_to:
type: string
description: ''
example: architecto
required:
- assigned_to
parameters:
-
in: path
name: assignmentId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/evaluation/attempts/{attemptId}/regrade':
post:
summary: 'Regrade a single attempt.'
operationId: regradeASingleAttempt
description: "Resets scores and re-dispatches grading job.\nUseful for fixing grading errors or updating after config changes."
parameters: []
responses: { }
tags:
- Evaluation
parameters:
-
in: path
name: attemptId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
'/v1/evaluation/quiz-versions/{versionId}/regrade':
post:
summary: 'Regrade all attempts for a quiz version.'
operationId: regradeAllAttemptsForAQuizVersion
description: "Batch regrading for all submitted attempts of a quiz version.\nUseful after updating grading configuration."
parameters: []
responses: { }
tags:
- Evaluation
parameters:
-
in: path
name: versionId
description: ''
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/evaluations:
post:
summary: 'Create new evaluation'
operationId: createNewEvaluation
description: ''
parameters: []
responses: { }
tags:
- Evaluation
security: []
'/v1/evaluations/{id}':
put:
summary: 'Update evaluation'
operationId: updateEvaluation
description: ''
parameters: []
responses: { }
tags:
- Evaluation
security: []
delete:
summary: 'Delete evaluation'
operationId: deleteEvaluation
description: ''
parameters: []
responses: { }
tags:
- Evaluation
security: []
parameters:
-
in: path
name: id
description: 'The ID of the evaluation.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/global/users:
get:
summary: 'List all users globally (cross-tenant)'
operationId: listAllUsersGloballycrossTenant
description: ''
parameters:
-
in: query
name: per_page
description: 'Number of items per page (1-100). Defaults to 10.'
example: 20
required: false
schema:
type: integer
description: 'Number of items per page (1-100). Defaults to 10.'
example: 20
-
in: query
name: page
description: 'Current page number. Defaults to 1.'
example: 1
required: false
schema:
type: integer
description: 'Current page number. Defaults to 1.'
example: 1
-
in: query
name: is_anonymous
description: 'Filter by anonymous status.'
example: false
required: false
schema:
type: boolean
description: 'Filter by anonymous status.'
example: false
-
in: query
name: search
description: 'Search in email, phone, and username.'
example: john
required: false
schema:
type: string
description: 'Search in email, phone, and username.'
example: john
-
in: query
name: created_at_gte
description: 'Filter users created on or after date.'
example: '2024-01-01'
required: false
schema:
type: string
description: 'Filter users created on or after date.'
example: '2024-01-01'
-
in: query
name: created_at_lte
description: 'Filter users created on or before date.'
example: '2024-12-31'
required: false
schema:
type: string
description: 'Filter users created on or before date.'
example: '2024-12-31'
-
in: query
name: sort
description: "Sort field. Prefix with '-' for descending."
example: '-created_at'
required: false
schema:
type: string
description: "Sort field. Prefix with '-' for descending."
example: '-created_at'
responses:
200:
description: ''
content:
text/plain:
schema:
type: string
example: '{"success": true, "message": "Users retrieved successfully", "data": [...], "meta": {"pagination": {"total": 50, "per_page": 10, "current_page": 1, "last_page": 5}}}'
tags:
- 'Global User Management'
security: []
post:
summary: 'Create new user with explicit tenant assignment'
operationId: createNewUserWithExplicitTenantAssignment
description: ''
parameters: []
responses: { }
tags:
- 'Global User Management'
security: []
'/v1/global/users/{id}':
put:
summary: 'Update user globally (allows tenant transfer)'
operationId: updateUserGloballyallowsTenantTransfer
description: ''
parameters: []
responses: { }
tags:
- 'Global User Management'
security: []
delete:
summary: 'Delete user globally (soft delete)'
operationId: deleteUserGloballysoftDelete
description: ''
parameters: []
responses: { }
tags:
- 'Global User Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the user.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/health:
get:
summary: 'Check API Health'
operationId: checkAPIHealth
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: healthy
properties:
status:
type: string
example: healthy
503:
description: ''
content:
application/json:
schema:
type: object
example:
status: unhealthy
properties:
status:
type: string
example: unhealthy
tags:
- Health
security: []
/v1/identity/register:
post:
summary: "Register new user\n\nCreates a new user account with identity (password).\nRequires at least one identifier (email, phone, or username) plus a password."
operationId: registerNewUserCreatesANewUserAccountWithIdentitypasswordRequiresAtLeastOneIdentifieremailPhoneOrUsernamePlusAPassword
description: ''
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Registration successful'
data:
user:
id: 01kav3fcmme10dwbeexyqkfndn
email: user@example.com
tenant_id: 01kav3fbpbpb3wnhtadsmrmt7m
tokens:
access_token: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type: Bearer
expires_in: 86400
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Registration successful'
data:
type: object
properties:
user:
type: object
properties:
id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
email:
type: string
example: user@example.com
tenant_id:
type: string
example: 01kav3fbpbpb3wnhtadsmrmt7m
tokens:
type: object
properties:
access_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type:
type: string
example: Bearer
expires_in:
type: integer
example: 86400
tags:
- Identity
security: []
/v1/identity/login:
post:
summary: "Login with credentials\n\nAuthenticate with email/phone/username and password.\nReturns JWT access token (24h) and refresh token (30d).\nRate limited to 10 attempts per hour."
operationId: loginWithCredentialsAuthenticateWithEmailphoneusernameAndPasswordReturnsJWTAccessToken24hAndRefreshToken30dRateLimitedTo10AttemptsPerHour
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Login successful'
data:
user:
id: 01kav3fcmme10dwbeexyqkfndn
email: user@example.com
tokens:
access_token: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type: Bearer
expires_in: 86400
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Login successful'
data:
type: object
properties:
user:
type: object
properties:
id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
email:
type: string
example: user@example.com
tokens:
type: object
properties:
access_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type:
type: string
example: Bearer
expires_in:
type: integer
example: 86400
tags:
- Identity
security: []
/v1/identity/refresh:
post:
summary: "Refresh access token\n\nExchange refresh token for new access and refresh tokens.\nImplements OAuth2 token rotation - old refresh token is blacklisted."
operationId: refreshAccessTokenExchangeRefreshTokenForNewAccessAndRefreshTokensImplementsOAuth2TokenRotationOldRefreshTokenIsBlacklisted
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Token refreshed successfully'
data:
access_token: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type: Bearer
expires_in: 86400
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Token refreshed successfully'
data:
type: object
properties:
access_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type:
type: string
example: Bearer
expires_in:
type: integer
example: 86400
tags:
- Identity
security: []
/v1/identity/anonymous:
post:
summary: "Anonymous login\n\nGenerate anonymous session token without creating database records.\nReturns a tracking ID and tokens that can be used to link requests across modules.\nUser can later \"upgrade\" to authenticated by registering with this ID."
operationId: anonymousLoginGenerateAnonymousSessionTokenWithoutCreatingDatabaseRecordsReturnsATrackingIDAndTokensThatCanBeUsedToLinkRequestsAcrossModulesUserCanLaterupgradeToAuthenticatedByRegisteringWithThisID
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Anonymous session created'
data:
anonymous_id: 01kav3fcmme10dwbeexyqkfndn
tokens:
access_token: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type: Bearer
expires_in: 86400
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Anonymous session created'
data:
type: object
properties:
anonymous_id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
tokens:
type: object
properties:
access_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type:
type: string
example: Bearer
expires_in:
type: integer
example: 86400
tags:
- Identity
security: []
/v1/identity/anonymous/claim:
post:
summary: "Claim anonymous account\n\nConverts anonymous session to authenticated account by claiming it with credentials.\nRequires anonymous token in Authorization header.\nCreates User and Identity records with the anonymous ID.\nPreserves data linkage across modules via shared ID."
operationId: claimAnonymousAccountConvertsAnonymousSessionToAuthenticatedAccountByClaimingItWithCredentialsRequiresAnonymousTokenInAuthorizationHeaderCreatesUserAndIdentityRecordsWithTheAnonymousIDPreservesDataLinkageAcrossModulesViaSharedID
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Account upgraded successfully'
data:
user:
id: 01kav3fcmme10dwbeexyqkfndn
email: user@example.com
is_anonymous: false
tokens:
access_token: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type: Bearer
expires_in: 86400
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Account upgraded successfully'
data:
type: object
properties:
user:
type: object
properties:
id:
type: string
example: 01kav3fcmme10dwbeexyqkfndn
email:
type: string
example: user@example.com
is_anonymous:
type: boolean
example: false
tokens:
type: object
properties:
access_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
refresh_token:
type: string
example: eyJ0eXAiOiJKV1QiLCJhbGc...
token_type:
type: string
example: Bearer
expires_in:
type: integer
example: 86400
401:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Missing or invalid authorization token'
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Missing or invalid authorization token'
409:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Anonymous account has already been upgraded'
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Anonymous account has already been upgraded'
tags:
- Identity
security: []
parameters:
-
in: path
name: anonymous
description: 'Anonymous identifier (from route: /identity/anonymous/claim)'
example: architecto
required: true
schema:
type: string
/v1/identity/change-password:
post:
summary: "Change password\n\nChange password for authenticated user.\nRequires current password for verification.\nProtected route - requires valid access token."
operationId: changePasswordChangePasswordForAuthenticatedUserRequiresCurrentPasswordForVerificationProtectedRouteRequiresValidAccessToken
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
message: 'Password changed successfully'
data: null
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Password changed successfully'
data:
type: string
example: null
tags:
- Identity
/v1/media:
post:
summary: 'Upload new media file'
operationId: uploadNewMediaFile
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
security: []
/v1/media/from-url:
post:
summary: 'Upload media from URL'
operationId: uploadMediaFromURL
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: 'Must be a valid URL. Must not be greater than 2048 characters.'
example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html'
privacy:
type: string
description: ''
example: identity
enum:
- public
- tenant
- identity
collection_name:
type: string
description: 'Must not be greater than 100 characters.'
example: i
title:
type: string
description: 'Must not be greater than 255 characters.'
example: k
description:
type: string
description: 'Must not be greater than 1000 characters.'
example: 'Aut ab provident perspiciatis quo omnis nostrum aut.'
alt_text:
type: string
description: 'Must not be greater than 500 characters.'
example: w
expires_at:
type: string
description: 'Must be a valid date.'
example: '2026-05-05T11:44:22'
required:
- url
security: []
/v1/media/batch:
post:
summary: 'Batch upload multiple media files'
operationId: batchUploadMultipleMediaFiles
description: ''
parameters: []
responses:
201:
description: ''
content:
text/plain:
schema:
type: string
example: '{"success": true, "message": "3 files uploaded successfully", "data": {"media": [{"id": "01...", ...}], "errors": []}}'
tags:
- 'Media Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
files:
type: array
description: 'Array of files to upload.'
items:
type: string
format: binary
privacy:
type: string
description: 'Privacy level for all files.'
example: tenant
collection_name:
type: string
description: 'nullable Collection name for all files.'
example: question-block-images
nullable: true
titles:
type: array
description: 'nullable Array of titles (one per file).'
example:
- 'Image 1'
- 'Image 2'
items:
type: string
nullable: true
required:
- files
- privacy
security: []
'/v1/media/{id}':
patch:
summary: 'Update media metadata (file content is immutable)'
operationId: updateMediaMetadatafileContentIsImmutable
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
security: []
delete:
summary: 'Delete media by ID (soft delete)'
operationId: deleteMediaByIDsoftDelete
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the medium.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/media/bulk-delete:
post:
summary: 'Bulk delete media files'
operationId: bulkDeleteMediaFiles
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ids:
type: array
description: ''
example:
- architecto
items:
type: string
required:
- ids
security: []
/v1/media/cleanup:
post:
summary: 'Cleanup expired media files'
operationId: cleanupExpiredMediaFiles
description: ''
parameters: []
responses: { }
tags:
- 'Media Management'
security: []
/v1/moodle-import:
post:
summary: 'Import an MBZ file'
operationId: importAnMBZFile
description: ''
parameters: []
responses: { }
tags:
- MoodleImport
security: []
/v1/notifications:
post:
summary: "Send a notification (single or bulk)\n\nSingle notification:\n{\n \"user_id\": \"ulid\",\n \"category\": \"info\",\n \"template_name\": \"welcome\",\n \"data\": {...}\n}\n\nBulk notifications:\n{\n \"notifications\": [\n {\"user_id\": \"ulid1\", \"category\": \"info\", ...},\n {\"user_id\": \"ulid2\", \"category\": \"urgent\", ...}\n ]\n}"
operationId: sendANotificationsingleOrBulkSingleNotificationuserIdulidcategoryinfotemplateNamewelcomedataBulkNotificationsnotificationsuserIdulid1categoryinfouserIdulid2categoryurgent
description: ''
parameters: []
responses: { }
tags:
- 'Notification Management'
security: []
/v1/notifications/read-all:
post:
summary: 'Mark all notifications as read for current user'
operationId: markAllNotificationsAsReadForCurrentUser
description: ''
parameters: []
responses: { }
tags:
- 'Notification Management'
security: []
'/v1/notifications/{id}/read':
patch:
summary: 'Mark notification as read'
operationId: markNotificationAsRead
description: ''
parameters: []
responses: { }
tags:
- 'Notification Management'
security: []
parameters:
-
in: path
name: id
description: 'The ID of the notification.'
example: 58C6f7nnFCJ9M6rjyc6rTKMzn6
required: true
schema:
type: string
/v1/notifications/preferences:
put:
summary: 'Update notification preferences'
operationId: updateNotificationPreferences
description: ''
parameters: []
responses: { }
tags:
- 'Notification Preferences'
security: []
delete:
summary: 'Delete notification preference (reset to defaults)'
operationId: deleteNotificationPreferenceresetToDefaults
description: ''
parameters: []
responses: { }
tags:
- 'Notification Preferences'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
category:
type: string
description: ''
example: info
enum:
- critical
- urgent
- info
- marketing
required:
- category
security: []
/v1/pdf/templates:
get:
summary: "List all available PDF templates\n\nReturns a list of all PDF templates available for document generation."
operationId: listAllAvailablePDFTemplatesReturnsAListOfAllPDFTemplatesAvailableForDocumentGeneration
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
data:
templates:
-
slug: quiz_certificate
name: 'Quiz Certificate'
description: ...
properties:
success:
type: boolean
example: true
data:
type: object
properties:
templates:
type: array
example:
-
slug: quiz_certificate
name: 'Quiz Certificate'
description: ...
items:
type: object
properties:
slug:
type: string
example: quiz_certificate
name:
type: string
example: 'Quiz Certificate'
description:
type: string
example: ...
tags:
- 'Pdf Management'
security: []
'/v1/pdf/generate/{template}':
post:
summary: "Generate PDF from template\n\nGenerates a PDF document from a template with the provided data.\nReturns the PDF as a base64-encoded string."
operationId: generatePDFFromTemplateGeneratesAPDFDocumentFromATemplateWithTheProvidedDataReturnsThePDFAsABase64EncodedString
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
data:
filename: quiz_certificate_2024-01-01_120000.pdf
content: base64...
size: 12345
contentType: application/pdf
generatedAt: '2024-01-01T12:00:00+00:00'
mediaId: null
properties:
success:
type: boolean
example: true
data:
type: object
properties:
filename:
type: string
example: quiz_certificate_2024-01-01_120000.pdf
content:
type: string
example: base64...
size:
type: integer
example: 12345
contentType:
type: string
example: application/pdf
generatedAt:
type: string
example: '2024-01-01T12:00:00+00:00'
mediaId:
type: string
example: null
tags:
- 'Pdf Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
data:
type: object
description: 'The data to populate the template (e.g., {"student_name": "Jane Doe", "quiz_title": "Sample Quiz"}).'
example: null
properties: { }
filename:
type: string
description: 'optional Custom filename for the generated PDF.'
example: architecto
format:
type: string
description: 'optional Paper format (A4, A3, Letter, Legal). Default: A4'
example: architecto
orientation:
type: string
description: 'optional Page orientation (portrait, landscape). Default: portrait'
example: architecto
store:
type: boolean
description: 'optional Store the PDF in the Media module and return media_id. Default: false'
example: false
required:
- data
security: []
parameters:
-
in: path
name: template
description: 'The template slug.'
example: quiz_certificate
required: true
schema:
type: string
'/v1/pdf/download/{template}':
post:
summary: "Download PDF directly\n\nGenerates a PDF and returns it as a downloadable file instead of a JSON response."
operationId: downloadPDFDirectlyGeneratesAPDFAndReturnsItAsADownloadableFileInsteadOfAJSONResponse
description: ''
parameters: []
responses: { }
tags:
- 'Pdf Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
data:
type: object
description: 'The data to populate the template (e.g., {"student_name": "Jane Doe", "quiz_title": "Sample Quiz"}).'
example: null
properties: { }
filename:
type: string
description: 'optional Custom filename for the generated PDF.'
example: architecto
format:
type: string
description: 'optional Paper format (A4, A3, Letter, Legal). Default: A4'
example: architecto
orientation:
type: string
description: 'optional Page orientation (portrait, landscape). Default: portrait'
example: architecto
store:
type: boolean
description: 'optional Store the PDF in the Media module. Default: false'
example: false
required:
- data
security: []
parameters:
-
in: path
name: template
description: 'The template slug.'
example: quiz_certificate
required: true
schema:
type: string
/v1/pdf/render:
post:
summary: "Render PDF from custom HTML\n\nGenerates a PDF from raw HTML provided in the request.\nThe HTML is processed through Blade, allowing variable substitution."
operationId: renderPDFFromCustomHTMLGeneratesAPDFFromRawHTMLProvidedInTheRequestTheHTMLIsProcessedThroughBladeAllowingVariableSubstitution
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
success: true
data:
filename: custom_2024-01-01_120000.pdf
content: base64...
size: 12345
contentType: application/pdf
generatedAt: '2024-01-01T12:00:00+00:00'
mediaId: null
properties:
success:
type: boolean
example: true
data:
type: object
properties:
filename:
type: string
example: custom_2024-01-01_120000.pdf
content:
type: string
example: base64...
size:
type: integer
example: 12345
contentType:
type: string
example: application/pdf
generatedAt:
type: string
example: '2024-01-01T12:00:00+00:00'
mediaId:
type: string
example: null
tags:
- 'Pdf Management'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
html:
type: string
description: 'The HTML template (max 500KB).'
example: '