Examplary
Start for free
Developer docs
Embed sessions

Exam generation flow

Using the Examplary exam generation flow, you can let users of your application use AI to generate questions from a set of source materials, optionally scoped to a specific subject.

Exam generation flow

Presets

Presets allow you to pre-configure certain options for the embed session, so that when your users open the embed, they see a tailored experience. Every key within presets is optional, but the presets object itself is required — pass "presets": {} if you don't want to pre-configure anything.

KeyTypeDescription
namestringThe name of the exam. Plain text.
subjectstringThe subject of the exam. Plain text.
studentLevelstringThe difficulty level of the exam. Expects a student level ID from /student-levels.
intendedDurationstringThe intended duration of the exam. One of "15-30 minutes", "30-45 minutes", "45-60 minutes", "60-90 minutes".
taxonomyIdstringThe ID of the taxonomy to use for the exam, from /taxonomies.
languagestringThe language code for the exam (e.g. "en" for English). Defaults to the user's locale.
sourceMaterialIdsstring[]An array of source material IDs to pre-select for the exam.
allowedGenerationQuestionTypesstring[]An array of question type IDs to limit the question generation options to.
contextstringAdditional context or instructions for the exam generation. Plain text.
hideSubjectbooleanWhether to hide the subject field in the exam generation UI. Defaults to false.
hideStudentLevelbooleanWhether to hide the student level field in the exam generation UI. Defaults to false.
hideDurationbooleanWhether to hide the duration field in the exam generation UI. Defaults to false.
hideTaxonomybooleanWhether to hide the taxonomy field in the exam generation UI. Defaults to false.
hideQuestionTypesbooleanWhether to hide the question types field in the exam generation UI. Defaults to false.
showPreviewbooleanWhether to show the generated questions for review before the session completes. Defaults to false. See Preview step.
availableStudentLevelsStudentLevel[]An array of custom student levels to make available for selection in the exam generation UI. When specified, only these student levels will be shown.

Example

1. Create an Examplary user for your user

To make sure we can save personal preferences and source materials uploaded by the user to their specific account, and can later allow them to reference these personal details, we require creating a user account in your workspace for each of your users.

POST/users
{
  "email": "my-user@example.com",
  "name": "My User"
}

Store the returned user ID in your system to use with any future embed sessions for that user.

2. Create an embed session

Call the Examplary API to create a new embed session. You can configure presets for the exam, as well as theme options.

The actor field should contain the ID of the Examplary user account you created for this customer.

You can either specify a returnUrl or an allowedOrigin, based on how you want to be notified when generation is completed.

POST/embed-sessions
{
  "flow": "generate-exam",
  "actor": "{userId}",
  "presets": {
    "subject": "Mathematics"
  },
  "theme": {
    "primaryColor": "#4f46e5",
    "locale": "en"
  },
  "metadata": {
    "myUniqueIdentifier": "abc1234"
  },
  "returnUrl": "https://app.example.com",
  "allowedOrigin": "https://app.example.com"
}

The response is the new embed session. Two of its fields matter for the next steps: embedUrl, the page to show your user, which stays valid until the session's expiresAt, and id, to look up the session later.

3. Lead the user to the embed URL

You can either redirect the user directly to the URL, or display it in an iframe.

The latter might be better for the user experience, especially when displayed as a modal. This also allows you to listen to status updates in real time, through the postMessage() API.

const embedUrl = "https://app.examplary.ai/embeds/55S843D7HfNf...";
 
iframe.src = embedUrl;
 
window.addEventListener("message", (event) => {
  // Make sure the message is coming from a trusted origin
  if (event.origin !== new URL(embedUrl).origin) return;
 
  // Handle the message
  const { type, status, outputs } = event.data;
  if (type === "examplary:embed-status-update") {
    if (status === "started") {
      // Fired as question generation progresses — outputs.questionGenerationStatus
      // reflects the current generation step (e.g. "generating_outline",
      // "generating_questions"). Useful for showing a progress indicator.
      console.log("Generation in progress:", outputs.questionGenerationStatus);
    } else if (status === "completed") {
      const examId = outputs.examId;
      console.log("Exam generation completed! Exam ID:", examId);
    }
  }
});

If you're using redirects, you can expect a redirect to your specified returnUrl on success, with the full outputs object and the embed session ID folded into the query string: ?status=completed&embedId=embed_session_55S8...&examId=exam_dj8948hf98hf43&questionGenerationStatus=completed

If neither of these options work for you, you may also poll the Examplary API for status updates:

GET /embed-sessions/{id}
{
  "id": "embed_session_55S843D7HfNfs9RY48PoTprXnRcz2...",
  "flow": "generate-exam",
  "status": "completed",
  "createdBy": "user_9pQr82jXd0dJ...",
  "outputs": {
    "examId": "exam_dj8948hf98hf43",
    "questionGenerationStatus": "completed",
  },
  // ...other embed session fields omitted
}

4. Get exam contents

Once the question generation has completed, you can use the API to retrieve your generated questions.

In Examplary's normal format:

GET /exams/{examId}

Or as a QTI package:

POST /exams/{examId}/export/qti3-zip

5. Cleanup

The embed session will expire automatically after 7 days, but because it gives some limited access to your account, you might want to remove it manually:

DELETE /embed-sessions/{embedSessionId}

You can also delete the generated exam once you don't need it anymore:

DELETE /exams/{examId}

Reference: custom student levels

When you want to override student levels, you can supply the following preset option:

{
  "presets": {
    "availableStudentLevels": [
      {
        "id": "beginner",
        "name": "Beginner driver",
        "instructions": "Use simple language, and try to avoid jargon. Keep explanations clear and concise."
      },
      {
        "id": "advanced",
        "name": "Advanced driver",
        "instructions": "Use technical terminology. Be precise and detailed in your explanations, and expect depth of knowledge from the student."
      }
    ]
  }
}

Reference fields for a StudentLevel object include:

  • id: A unique identifier for the student level.
  • name: A human-readable name for the student level.
  • instructions: Guidance on how to tailor explanations for students at this level. Include information about language use and the types of questions to include. Keep this to 2-3 sentences. This is included in every prompt, so very long descriptions make the generation process noticeably slower.

Reference: preview step

With showPreview set to true, the embed shows a read-only list of the generated questions once generation has finished, instead of completing straight away. The user can go back to the settings to regenerate (this clears the generated questions), or accept the questions. The session only completes — and examplary:embed-status-update only fires with status: "completed" — after they accept.