MakeQuestions API by Karson AI

Changelog

New updates and improvements to the MakeQuestions API and Karson AI API. Each release notes breaking changes, additions, and migration tips.

v3.6.0 — Jul 2026

Automatic question planning, live web research, and two new turn-level controls. question_config is now optional — omit it and the server plans the question set from your source material.

Added — auto question planning

  • Omitting question_config on POST /generate-questions now runs the auto planner, which infers question types and counts from your source material. This reverses the hard requirement introduced in v3.1.0.
  • New question_config.mode: "auto" shape for opting in explicitly (e.g. when you still want to pin difficulty). Sending type_counts alongside mode: "auto" returns 422.
  • Sending an explicit "question_config": null is still rejected with 422 — omit the key instead.
  • Manual type_counts[type] = null is unchanged: it keeps its selected-type, model-decided-count behavior and does not call the planner.
  • Responses now include generation_plan{ source, type_counts, difficulty, reasoning_summary? } — so auto and manual clients alike can see the effective plan. source is client_exact / client_soft for manual configs, auto_default / auto_explicit for planner runs, or an auto_fallback_* variant when the planner failed and defaults were used.
  • Streaming emits the plan as a new plan SSE event, before any question event. done repeats it as generation_plan.
  • Two auto-planning controls on /generate-questions: allowed_question_types (non-empty allowlist constraining the planner or a manual map) and max_questions (positive cap on the total). They shipped with auto planning; v3.5.0 records their removal from /follow-up.

Added — web_search and live web research

  • web_search: true on POST /generate-questions and POST /follow-up (default false, strictly opt-in) runs an internal research phase: live web search assembles a cited markdown source brief that becomes primary source material for generation.
  • Non-streaming responses gain web_research: { status, queries, sources: [{ n, url, title, published_date }], brief, brief_chars }. The brief is exposed in full — it is exactly what generation saw.
  • Streaming emits four new events before the intent's own stream: web_search_started, web_research_progress ({ stage: "searching", query } per search; { stage: "source", url, title } per source), web_research_delta ({ text } — the brief being written), and web_research (the authoritative payload; done does not repeat it).
  • Questions from a web-search generation may carry source_ids — the [n] markers from the brief they drew on. Best-effort hints, never a contract: unresolvable ids are dropped server-side, and the key is absent on non-web requests.
  • Research is chained from the turn's response_id when one is present — on /follow-up and on /generate-questions continuation turns alike.
  • Research runs on its own model, independent of the reasoning flag, so reasoning: false keeps its zero-reasoning-token contract. Research failure degrades to no-brief generation with web_research.status: "failed" — it never fails the request.
  • Cost/latency adder when enabled: roughly +$0.02–0.05 and +4–10 s per request.

Added — question_history

  • POST /generate-questions accepts an optional question_history object (items + omitted_count) describing questions your app already holds. Listed questions become an avoid-list in the generation prompt, and near-duplicate candidates are dropped.
  • Bounds: at most 200 items (more returns 422); question truncated to 300 chars, answer to 150, tags to 20 × 64 chars, source_ids to 50 entries. Legacy status: "pending" normalizes to "active".
  • It is not source material — a history-only request with no response_id / context / attachments / image_urls still returns 422.
  • Drops are reported in agent_metadata.history_duplicate_drops and, when streaming, in done.question_history.
  • POST /follow-up does not declare the field — sending it there is a 422.

Added — ui_language

  • Both turn endpoints accept an optional ui_language ("en" | "vi", default "en"). It sets the output language only when the input is language-ambiguous (e.g. a topic-only prompt); a clear content language in the source material always wins.
  • Unknown or invalid values — including explicit null — are silently normalized to "en". No 4xx, so old and new clients interoperate.

Changed

  • POST /generate-questions now rejects unknown top-level keys with 422 (extra="forbid"), so a typo fails loudly instead of being ignored. /follow-up already did.
  • POST /generate-questions accepts response_id to continue an existing question chain; continuation turns may omit source material. Origin turns still require context, attachments, or image_urls. (This landed alongside the auto-planning work and is what v3.5.0 builds on — the two shipped in the same rollout.)

Removed

  • POST /generate-questions/stream (raw model-JSON chunks) is deleted. It was mounted only when the server ran in dev mode and was never part of the public contract. Use POST /generate-questions with "streaming": true.

v3.5.0 — Jul 2026

Breaking. The more-questions follow-up intent is removed. POST /follow-up no longer accepts request_type: "more-questions" — it returns 422. Generate more questions in an existing thread by calling POST /generate-questions with the prior turn's response_id.

Breaking Changes

  • POST /follow-up request_type narrows to inline-explanation | chat. Sending "more-questions" now returns 422.
  • POST /follow-up no longer accepts question_config (or allowed_question_types / max_questions); those keys are rejected (extra="forbid"). Question-generation config belongs on /generate-questions.
  • The MoreQuestionsResponse shape is retired — it was an alias of the /generate-questions response. Question continuations return the standard /generate-questions response.
  • One capability goes away entirely with the intent: starting a fresh chain from question_config alone, with no source material. more-questions allowed it; /generate-questions does not. An origin turn still requires context, attachments, or image_urls, so a request carrying only question_config now returns 422.

Migration

  • Generate more questions in a thread: call POST /generate-questions with the prior turn's response_id plus your question_config, instead of POST /follow-up with request_type: "more-questions". Continuation requests may omit source material — the chain carries prior context.
  • Were you calling more-questions with no response_id? That path is gone. Send real source material (context, attachments, or image_urls) on the /generate-questions call.
  • POST /follow-up remains the endpoint for inline-explanation and chat.

Why

  • One canonical endpoint per turn kind: /generate-questions owns question turns (fresh or continued via response_id), /follow-up owns the text intents. Removes a redundant path and an empty response alias.

v3.4.0 — Jul 2026

reasoning now defaults to true on POST /generate-questions and POST /follow-up, and all generation runs on a single model. Breaking for any client that relied on the old implicit reasoning: false default.

Breaking Changes

  • reasoning now defaults to true. A request that omits the field runs the reasoning path: the response may include reasoning_summary (and reasoning_summary_part / reasoning_summary SSE events when streaming), and the call bills reasoning tokens.
  • To keep the previous instant behavior — no reasoning tokens, no reasoning_summary — send reasoning: false explicitly.
  • Clients that already send reasoning explicitly (true or false) are unaffected.

Changed

  • Single generation model across every path (generate, follow-up, inline-explanation, chat, streaming). reasoning: true uses low reasoning effort; reasoning: false uses zero effort — truly instant, with no reasoning tokens (the prior default still spent a little).
  • Both modes are faster than the previous default: reasoning-mode and instant-mode latency both dropped in internal generation evals.
  • multiple_choice questions are now guaranteed at least 2 choices, enforced at decode time.

Why

  • Reasoning mode measurably improves question quality — answer correctness, distractor quality, and LaTeX/JSON fidelity — and is now fast enough to be the default.
  • reasoning: false becomes an honest instant contract: zero reasoning tokens, no summary.

v3.3.0 — Jun 2026

Added file attachments (images and documents) as the preferred way to send files, and raised request limits.

Added

  • attachments on POST /generate-questions and POST /follow-up: an array of { "url", "mime_type", "name"? } with public HTTPS URLs. Images (image/jpeg, image/png, image/gif, image/webp) are sent as vision input; documents (application/pdf, .doc, .docx, text/plain) are read directly as source material — no need to pre-extract text. Up to 1,500 images and 50 documents per request; URLs must be HTTPS and ≤ 8,192 chars. Prefer attachments over legacy image_urls.

Changed

  • Rate limit raised to 1,000 requests per minute per API key.
  • Image input limit raised again, from 50 to 1,500 unique images per request. The 1,500 cap applies to the union of image attachments and legacy image_urls after de-duplication — not 1,500 of each.

Why

  • Send PDFs, Word docs, and text files directly as question source material, instead of pre-extracting text.
  • Higher limits support batch and document-heavy workloads.

v3.2.1 — May 2026

Relaxed the vision-input limits and started reporting them from /config.

Changed

  • image_urls on both endpoints: max unique URLs raised from 5 to 50, and max URL length from 2,048 to 8,192 characters.
  • Duplicate URLs are now removed before the max-image limit is enforced.
  • GET /config now reports the active vision limits under vision.max_images and vision.max_url_length.

Migration

  • No code changes required. Update your own client-side validation if you want to send more than 5 image URLs.

v3.2.0 — Apr 2026

Renamed the follow-up request field previous_response_id to response_id so request and response use the same name for the same handle. Breaking change for any client that calls POST /follow-up.

Breaking Changes

  • POST /follow-up now expects response_id in the request body. previous_response_id is no longer accepted and (because extra="forbid") returns 422.
  • Update payloads from { "previous_response_id": "resp_abc123", ... } to { "response_id": "resp_abc123", ... }.
  • Response bodies and SSE done events are unchanged — they already used response_id.

Why

  • One name for the continuation handle on both ends of a turn — copy a response_id straight from the last response into the next request.
  • Removes the long-standing wording mismatch in docs and SDKs.

v3.1.0 — Apr 2026

Simplified the request contract: question_config is now required and the "mixed" question type is gone. Callers declare exactly which concrete types they want and optionally how many of each.

Partly superseded by v3.6.0. question_config is optional again — omitting it runs the auto planner rather than returning 422. Everything below about type_counts semantics and the removal of "mixed" still holds.

Breaking Changes

  • question_config is required on POST /generate-questions and on POST /follow-up with request_type: "more-questions". Requests without it return 422. (Relaxed in v3.6.0 — omitting it now triggers auto planning.)
  • type_counts must be a non-empty map of concrete question types. Empty maps, all-0 maps, unknown keys, and the old "mixed" key are all rejected with 422. (A 0 value on an individual key is silently dropped — equivalent to omitting the key — as long as at least one entry remains.)
  • No more implicit "open mix" default. Omit a type to exclude it; use null per-type to let the model decide that type's count (the server guarantees at least 1 of every requested type).
  • Playground no longer offers a "Mixed" type option — pick one or more concrete types.

Why

  • Smaller system prompts — documentation for unused types is no longer injected.
  • Predictable count enforcement — the validator and agentic filler always know exactly which types to target.
  • Simpler mental model — one required, non-empty map with clear per-key semantics (int = exact count, null = model decides quantity with a guaranteed minimum of 1 of that type, omitted = disabled).

v2.9.1 — Apr 2026

Added optional vision input via image_urls on the generation endpoints.

Added

  • Optional image_urls on POST /generate-questions and POST /follow-up (all request_type values). Image URLs are forwarded to the model alongside the text input on the same turn.

Validation

  • HTTPS URLs only.
  • Max 5 URLs per request; each URL ≤ 2,048 characters. (Count raised to 50 in v3.2.1 and to 1,500 in v3.3.0; URL length raised to 8,192 in v3.2.1 and unchanged since.)
  • Duplicates are removed server-side.

v2.9.0 — Mar 2026

Generation requests adopt question_config. The legacy top-level shape (question_type_counts, top-level difficulty, allowed_types) is no longer accepted on POST /generate-questions.

Breaking Changes

  • Top-level question_type_counts, difficulty, and allowed_types are removed from POST /generate-questions. Use question_config with a type_counts map and optional difficulty.
  • Same rules apply to POST /follow-up with request_type: "more-questions".
  • Do not send "mixed" as a difficulty value — use null or omit.

Behavior

  • Omitting question_config (or sending null) used the default open mix at this release. This was tightened to a hard requirement in v3.1.0, then reopened in v3.6.0 as model-driven auto planning (an explicit null is still rejected — omit the key).

v2.8.1 — Jan 2026

Streaming inline-explanation now emits text deltas as they arrive, so UIs can render the explanation character-by-character instead of waiting for the full payload.

Added

  • New SSE event explanation_text on streaming POST /follow-up with request_type: "inline-explanation". Data shape: { "text": "<delta>" }.
  • The existing explanation event still fires once after streaming completes with the full text — backward-compatible for clients that only consume the final payload.

Fixed

  • Analytics: delivery_mode is now populated for inline-explanation requests (was previously NULL); failed inline-explanation and chat requests are now persisted with error metadata.

v2.8.0 — Jan 2026

Expanded POST /follow-up to three request types — more-questions, inline-explanation, and chat — enabling conversational interactions on top of question generation.

Added

  • request_type: "inline-explanation" — concise explanation for one question. Put the question text in input; question_type_counts is ignored.
  • request_type: "chat" — free-form tutoring chat over the thread; always streamed via SSE.
  • response_id returned on every /follow-up response (and on the SSE done event) for chaining further turns. (At this release the corresponding request field was still named previous_response_id — see v3.2.0.)

Validation

  • more-questions: question_type_counts required; previous_response_id optional.
  • inline-explanation: previous_response_id and input required; question_type_counts ignored.
  • chat: previous_response_id and input required; always streamed.

v2.7.0 — Jan 2026

Consolidated streaming endpoints into a single endpoint with a streaming parameter.

Breaking Changes

  • /generate-questions/stream-by-question removed — Use /generate-questions with streaming: true.

Migration

  • Before: POST /generate-questions/stream-by-question
  • After: POST /generate-questions with "streaming": true in request body

New Parameter

  • streaming — Boolean parameter to enable SSE streaming mode (default: false).
  • When true, returns questions one at a time via Server-Sent Events.
  • When false (default), returns all questions at once (batch mode).

Improvements

  • Single endpoint for both batch and streaming modes.
  • Consistent API pattern matching the follow-up endpoint.
  • Simplified client integration — same endpoint, just add a parameter.