Vana Public API
REST API for accessing courses, quizzes, and learning content. The API is not versioned in the URL; it only grows. New endpoints, response fields, optional request fields and enum values (such as block types) can appear at any time, so ignore fields you do not recognise and skip enum values you cannot handle. Nothing already in the spec is removed or renamed; replaced parts are marked deprecated and keep working.
Let an agent write the course
Connect your agent and it gets all 58 endpoints as tools, plus a guide to every block type and the traps that fail silently. Point it at an outline and let it build.
Add Vana as an MCP server, once:
claude mcp add --transport http vana https://vana.proposales.com/mcp \
--header "Authorization: Bearer $VANA_KEY"Create a key on the Account page under API keys, and keep it in VANA_KEY rather than in a file you might commit.
Base URL
https://vana.proposales.com/apiAuthentication
API key authentication (format: Bearer vana_xxxxx)
curl -H "Authorization: Bearer vana_…" https://vana.proposales.com/api/projectsWorkspaces
The spaces projects live in: your personal one and any teams
get
/workspacesList your workspacesShowHideThe workspaces you belong to, your role in each, and whether you can create projects there. Ask the user which one before creating a project.
Responses
200Successful responseobject401UnauthorizedError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/workspaces
Projects
Top-level study spaces
get
/projectsList all projectsShowHideEvery project you can read, across all your workspaces, each with the workspace it belongs to.
Responses
200Successful responseobject401UnauthorizedError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projectspost
/projectsCreate a projectShowHideCreates a project in a workspace. If the user has not said which workspace, call list_workspaces and ask them before creating. Without workspace_id this fails with 400 when there is more than one workspace to choose from, and lists them.
Request bodyProjectCreate · required
name* string description string workspace_id string (uuid) The workspace to create the project in (from list_workspaces). Required when you can create in more than one workspace: ask the user which one rather than choosing for them. Projects in a team workspace are shared with its members. Responses
201CreatedProject400Invalid request body, or workspace_id missing while you can create in more than one workspaceError401UnauthorizedError403You cannot create projects in that workspaceError404Workspace not foundError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projectsThis runs against your real data, not a sandbox.
get
/projects/{projectId}Get project with full hierarchyShowHideRetrieve a project with all courses, modules, lessons, and blocks
Parameters
- projectId
- pathstring (uuid)required
Responses
200Successful responseProject401Unauthorized404Project not found
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}Fill the path parameters to send.
patch
/projects/{projectId}Update a projectShowHideParameters
- projectId
- pathstring (uuid)required
Request bodyProjectUpdate · required
name string description string Responses
200UpdatedProject400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/projects/{projectId}Delete a projectShowHideHides the project and everything in it from every read. Nothing is erased: listDeletedProjects shows it and restoreProject brings it back.
Parameters
- projectId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Courses
Courses inside a project
get
/courses/{courseId}Get course detailsShowHideRetrieve a specific course with all modules, lessons, and blocks
Parameters
- courseId
- pathstring (uuid)required
Responses
200Successful responseCourse401Unauthorized403Unauthorized access to course404Course not found
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}Fill the path parameters to send.
patch
/courses/{courseId}Update a courseShowHideParameters
- courseId
- pathstring (uuid)required
Request bodyCourseUpdate · required
title string description string sources CourseSource[] The documents this course was built from. Replaces the whole list when given on update. position integer Sort order within the parent. Appended to the end when omitted. Responses
200UpdatedCourse400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/courses/{courseId}Delete a courseShowHideChildren are deleted with it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- courseId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
post
/courses/{courseId}/publishPublish a courseShowHideMakes a course visible to learners. Publishing a draft course also publishes every draft lesson in it; on a course that is already published, it publishes the lessons that are still drafts. Not available over MCP: publishing is a person's decision.
Parameters
- courseId
- pathstring (uuid)required
Responses
200PublishedCourse401UnauthorizedError404Not found, or not the caller's to editError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}/publishFill the path parameters to send.
This runs against your real data, not a sandbox.
post
/courses/{courseId}/unpublishUnpublish a courseShowHideHides a course from learners. Its lessons keep their own state and learners keep their progress, so publishing it again brings back what was out before. Not available over MCP.
Parameters
- courseId
- pathstring (uuid)required
Responses
200UnpublishedCourse401UnauthorizedError404Not found, or not the caller's to editError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}/unpublishFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/projects/{projectId}/coursesList courses in a projectShowHideParameters
- projectId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Courses ordered by positionobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/coursesFill the path parameters to send.
post
/projects/{projectId}/coursesCreate a courseShowHideParameters
- projectId
- pathstring (uuid)required
Request bodyCourseCreate · required
title* string description string sources CourseSource[] The documents this course was built from. Replaces the whole list when given on update. position integer Sort order within the parent. Appended to the end when omitted. Responses
201Course createdCourse400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/coursesFill the path parameters to send.
This runs against your real data, not a sandbox.
Modules
Modules inside a course
get
/courses/{courseId}/modulesList modules in a courseShowHideParameters
- courseId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Modules ordered by positionobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}/modulesFill the path parameters to send.
post
/courses/{courseId}/modulesCreate a moduleShowHideParameters
- courseId
- pathstring (uuid)required
Request bodyModuleCreate · required
title* string position integer Sort order within the parent. Appended to the end when omitted. Responses
201Module createdModule400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/courses/{courseId}/modulesFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/modules/{moduleId}Get a moduleShowHideParameters
- moduleId
- pathstring (uuid)required
Responses
200SuccessModule401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/modules/{moduleId}Fill the path parameters to send.
patch
/modules/{moduleId}Update a moduleShowHideParameters
- moduleId
- pathstring (uuid)required
Request bodyModuleUpdate · required
title string position integer Sort order within the parent. Appended to the end when omitted. Responses
200UpdatedModule400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/modules/{moduleId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/modules/{moduleId}Delete a moduleShowHideChildren are deleted with it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- moduleId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/modules/{moduleId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Lessons
Lessons inside a module
get
/modules/{moduleId}/lessonsList lessons in a moduleShowHideParameters
- moduleId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Lessons ordered by positionobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/modules/{moduleId}/lessonsFill the path parameters to send.
post
/modules/{moduleId}/lessonsCreate a lessonShowHideParameters
- moduleId
- pathstring (uuid)required
Request bodyLessonCreate · required
title* string position integer Sort order within the parent. Appended to the end when omitted. Responses
201Lesson createdLesson400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/modules/{moduleId}/lessonsFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/lessons/{lessonId}Get a lessonShowHideParameters
- lessonId
- pathstring (uuid)required
Responses
200SuccessLesson401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}Fill the path parameters to send.
patch
/lessons/{lessonId}Update a lessonShowHideParameters
- lessonId
- pathstring (uuid)required
Request bodyLessonUpdate · required
title string position integer Sort order within the parent. Appended to the end when omitted. Responses
200UpdatedLesson400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/lessons/{lessonId}Delete a lessonShowHideChildren are deleted with it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- lessonId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
post
/lessons/{lessonId}/publishPublish a lessonShowHideMakes a draft lesson visible to learners, once its course is published too. Not available over MCP.
Parameters
- lessonId
- pathstring (uuid)required
Responses
200PublishedLesson401UnauthorizedError404Not found, or not the caller's to editError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}/publishFill the path parameters to send.
This runs against your real data, not a sandbox.
post
/lessons/{lessonId}/unpublishUnpublish a lessonShowHideTurns a lesson back into a draft, hidden from learners. Not available over MCP.
Parameters
- lessonId
- pathstring (uuid)required
Responses
200UnpublishedLesson401UnauthorizedError404Not found, or not the caller's to editError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}/unpublishFill the path parameters to send.
This runs against your real data, not a sandbox.
Blocks
Content units inside a lesson
get
/lessons/{lessonId}/blocksList blocks in a lessonShowHideParameters
- lessonId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Blocks ordered by positionobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}/blocksFill the path parameters to send.
post
/lessons/{lessonId}/blocksCreate a blockShowHideParameters
- lessonId
- pathstring (uuid)required
Request bodyBlockCreate · required
type* string "text" | "image" | "diagram" | "video" | "example" | "question" | "quiz" | "scenario" | "glossary" | "interactive" position integer Sort order within the parent. Appended to the end when omitted. content object Per-type payload. Not schema-checked: a shape the renderer does not recognise is stored and the block renders empty, so it never 400s. text/example: {"body": "markdown"}. scenario: {"body", "reveal"}. glossary: {"title", "terms": [{"term", "definition"}]}. image: {"source": {"type": "url", "url"} or {"type": "svg", "code"}, "alt", "caption"}. video: {"source": {"type": "youtube", "id", "start"} or {"type": "url", "url"}, "title", "caption"}. diagram: {"source": {"type": "mermaid", "code"}, "caption"}. interactive: {"source": {"type": "html", "code"}, "title", "caption", "height"}. visual: {"prompt", "status"}. question/quiz blocks carry no content. question_id string (uuid) Required when type is 'question'. Must belong to the same project. question_role string Required when type is 'question'. "practice" | "checkpoint" | "assessment" | "review" quiz_id string (uuid) Required when type is 'quiz'. Must belong to the same project. Responses
201Block createdBlock400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/lessons/{lessonId}/blocksFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/blocks/{blockId}Get a blockShowHideParameters
- blockId
- pathstring (uuid)required
Responses
200SuccessBlock401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/blocks/{blockId}Fill the path parameters to send.
patch
/blocks/{blockId}Update a blockShowHideParameters
- blockId
- pathstring (uuid)required
Request bodyBlockUpdate · required
position integer Sort order within the parent. Appended to the end when omitted. content object Per-type payload. Not schema-checked: a shape the renderer does not recognise is stored and the block renders empty, so it never 400s. text/example: {"body": "markdown"}. scenario: {"body", "reveal"}. glossary: {"title", "terms": [{"term", "definition"}]}. image: {"source": {"type": "url", "url"} or {"type": "svg", "code"}, "alt", "caption"}. video: {"source": {"type": "youtube", "id", "start"} or {"type": "url", "url"}, "title", "caption"}. diagram: {"source": {"type": "mermaid", "code"}, "caption"}. interactive: {"source": {"type": "html", "code"}, "title", "caption", "height"}. visual: {"prompt", "status"}. question/quiz blocks carry no content. question_id string (uuid) Required when type is 'question'. Must belong to the same project. question_role string Required when type is 'question'. "practice" | "checkpoint" | "assessment" | "review" quiz_id string (uuid) Required when type is 'quiz'. Must belong to the same project. Responses
200UpdatedBlock400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/blocks/{blockId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/blocks/{blockId}Delete a blockShowHideChildren are deleted with it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- blockId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/blocks/{blockId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Quizzes
Quiz attempts and scoring
post
/quizzes/{quizId}/submitSubmit quiz attemptShowHideSubmit answers to a quiz and get scoring
Parameters
- quizId
- pathstring (uuid)required
Request bodyQuizAttemptSubmission · required
answers* QuizAttemptAnswer[] Responses
201Quiz attempt submitted successfullyQuizAttemptResult400Invalid request401Unauthorized403Unauthorized access to quiz404Quiz not found
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/quizzes/{quizId}/submitFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/projects/{projectId}/quizzesList quizzes in a projectShowHideParameters
- projectId
- pathstring (uuid)required
Responses
200Newest first, with a question count rather than full questionsobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/quizzesFill the path parameters to send.
post
/projects/{projectId}/quizzesCreate a quizShowHideParameters
- projectId
- pathstring (uuid)required
Request bodyQuizCreate · required
title* string question_ids string (uuid)[] Array order becomes sort_order. Each question must belong to the same project and may appear only once. Responses
201CreatedQuizDetail400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/quizzesFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/quizzes/{quizId}Get a quiz with its questionsShowHideParameters
- quizId
- pathstring (uuid)required
Responses
200Questions in sort_order, with optionsQuizDetail401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/quizzes/{quizId}Fill the path parameters to send.
patch
/quizzes/{quizId}Update a quizShowHideSending question_ids replaces the whole set, in array order.
Parameters
- quizId
- pathstring (uuid)required
Request bodyQuizUpdate · required
title string question_ids string (uuid)[] Array order becomes sort_order. Each question must belong to the same project and may appear only once. Responses
200UpdatedQuizDetail400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/quizzes/{quizId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/quizzes/{quizId}Delete a quizShowHideFails with 409 while a lesson block references it. Recorded attempts are kept but hidden with the quiz. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- quizId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/quizzes/{quizId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
API Keys
Manage credentials for this API
get
/api-keysList API keysShowHideGet all active API keys for the authenticated user (keys are masked)
Responses
200Successful responseobject401Unauthorized
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/api-keyspost
/api-keysCreate new API keyShowHideMint an additional key using an existing one, for rotation. The first key must be created from the Account page in the web app.
Request bodyCreateApiKeyRequest · required
name* string Responses
201API key created successfullyCreateApiKeyResponse400Invalid request401Unauthorized
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/api-keysThis runs against your real data, not a sandbox.
delete
/api-keys/{keyId}Delete API keyShowHideDeactivate an API key
Parameters
- keyId
- pathstring (uuid)required
Responses
200API key deleted successfullyobject401Unauthorized404API key not found
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/api-keys/{keyId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Questions
Authoring questions and their answer options
get
/projects/{projectId}/questionsList questions in a projectShowHideParameters
- projectId
- pathstring (uuid)required
- limit
- queryinteger
- offset
- queryinteger
- category_id
- querystring (uuid)— Filter to one category.
Responses
200Newest firstobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/questionsFill the path parameters to send.
post
/projects/{projectId}/questionsCreate a question with its optionsShowHideOptions are supplied inline; a question is never created without a valid set.
Parameters
- projectId
- pathstring (uuid)required
Request bodyQuestionCreate · required
category_id* string (uuid) Required. Must belong to the same project. question_family_id string (uuid) | null Optional grouping for AI variants. Must belong to the same project. prompt* string question_type* string "true_false" | "single_choice" | "multiple_choice" difficulty* integer options* QuestionOptionInput[] Replaces the full set. true_false: exactly 2 options, exactly 1 correct. single_choice: 2+ options, exactly 1 correct. multiple_choice: 2+ options, 2+ correct. Responses
201CreatedQuestionDetail400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/questionsFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/questions/{questionId}Get a questionShowHideParameters
- questionId
- pathstring (uuid)required
Responses
200SuccessQuestionDetail401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/questions/{questionId}Fill the path parameters to send.
patch
/questions/{questionId}Update a questionShowHideSending options replaces the whole set. The merged question is revalidated.
Parameters
- questionId
- pathstring (uuid)required
Request bodyQuestionUpdate · required
category_id string (uuid) Required. Must belong to the same project. question_family_id string (uuid) | null Optional grouping for AI variants. Must belong to the same project. prompt string question_type string "true_false" | "single_choice" | "multiple_choice" difficulty integer options QuestionOptionInput[] Replaces the full set. true_false: exactly 2 options, exactly 1 correct. single_choice: 2+ options, exactly 1 correct. multiple_choice: 2+ options, 2+ correct. Responses
200UpdatedQuestionDetail400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/questions/{questionId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/questions/{questionId}Delete a questionShowHideFails with 409 while a lesson block references it. Quiz membership is removed automatically. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- questionId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/questions/{questionId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Categories
Categories questions are filed under (required on every question)
get
/projects/{projectId}/categoriesList category in a projectShowHideParameters
- projectId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Ordered by nameobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/categoriesFill the path parameters to send.
post
/projects/{projectId}/categoriesCreate a categoryShowHideParameters
- projectId
- pathstring (uuid)required
Request bodyCategoryWrite · required
name* string Unique within the project. Responses
201CreatedCategory400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/categoriesFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/categories/{categoryId}Get a categoryShowHideParameters
- categoryId
- pathstring (uuid)required
Responses
200SuccessCategory401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/categories/{categoryId}Fill the path parameters to send.
patch
/categories/{categoryId}Rename a categoryShowHideParameters
- categoryId
- pathstring (uuid)required
Request bodyCategoryWrite · required
name* string Unique within the project. Responses
200UpdatedCategory400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/categories/{categoryId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/categories/{categoryId}Delete a categoryShowHideFails with 409 while live questions use it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- categoryId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/categories/{categoryId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Question families
Optional grouping used for AI question variants
get
/projects/{projectId}/question-familiesList question family in a projectShowHideParameters
- projectId
- pathstring (uuid)required
- limit
- queryinteger— Page size. Default 500, at most 500.
- offset
- queryinteger— Rows to skip. Use the previous page's offset plus its limit while has_more is true.
Responses
200Ordered by nameobject401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/question-familiesFill the path parameters to send.
post
/projects/{projectId}/question-familiesCreate a question familyShowHideParameters
- projectId
- pathstring (uuid)required
Request bodyCategoryWrite · required
name* string Unique within the project. Responses
201CreatedCategory400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/question-familiesFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/question-families/{familyId}Get a question familyShowHideParameters
- familyId
- pathstring (uuid)required
Responses
200SuccessCategory401UnauthorizedError404Not found, or not owned by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/question-families/{familyId}Fill the path parameters to send.
patch
/question-families/{familyId}Rename a question familyShowHideParameters
- familyId
- pathstring (uuid)required
Request bodyCategoryWrite · required
name* string Unique within the project. Responses
200UpdatedCategory400Invalid request bodyError401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/question-families/{familyId}Fill the path parameters to send.
This runs against your real data, not a sandbox.
delete
/question-families/{familyId}Delete a question familyShowHideQuestions in the family are unlinked from it. Nothing is erased: it goes to the project's trash (listTrash) and can be restored with everything deleted along with it.
Parameters
- familyId
- pathstring (uuid)required
Responses
200DeletedDeleteResult401UnauthorizedError404Not found, or not owned by the callerError409Conflicts with existing dataError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/question-families/{familyId}Fill the path parameters to send.
This deletes real data in your account, and cascades to anything beneath it.
Trash
Nothing is ever erased. Deletes hide content and stamp who deleted it; these list and restore it.
get
/projects/deletedList deleted projectsShowHideDeleted projects you could restore, newest first. Nothing is ever erased; restore one with restoreProject.
Responses
200Successful responseobject401UnauthorizedError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/deletedpost
/projects/{projectId}/restoreRestore a deleted projectShowHideBrings back a deleted project and everything in it.
Parameters
- projectId
- pathstring (uuid)required
Responses
200Restoredobject401UnauthorizedError404Not deleted, or not yours to restoreError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/restoreFill the path parameters to send.
This runs against your real data, not a sandbox.
get
/projects/{projectId}/trashList a project's trashShowHideWhat was deleted in the project, newest first: one entry per delete, not every row it took with it (a deleted course is one entry, not the course plus its modules, lessons and blocks). Needs write access.
Parameters
- projectId
- pathstring (uuid)required
Responses
200Successful responseobject401UnauthorizedError404Not found, or not writable by the callerError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/trashFill the path parameters to send.
post
/projects/{projectId}/trash/restoreRestore from a project's trashShowHideBrings back one item listed by listTrash, with everything deleted along with it. Things deleted separately before or after stay in the trash. 409 when its parent is still deleted (restore that first; the message names it) or when its name has been taken since.
Parameters
- projectId
- pathstring (uuid)required
Request bodyTrashRestore · required
kind* string "course" | "module" | "lesson" | "block" | "question" | "quiz" | "category" | "question_family" id* string (uuid) Responses
200Restoredobject400Invalid request bodyError401UnauthorizedError404Not in this project's trash, or project not writableError409Parent still deleted, or name takenError503API not configuredError
Try it
Kept for this tab only, shared with the other endpoints on this page, and sent only to
/apion this origin. Create one on the Account page./api/projects/{projectId}/trash/restoreFill the path parameters to send.
This runs against your real data, not a sandbox.