Quest integration guide
Build a learning session with Quest.
Prepare a task, choose how to help, send learner work and read saved results. Interface version 2026-12-01.
Get the right access
Check Quest availability and add test or live access to your workspace. Sign-in, terms and workspace selection preserve the selected product. Enrollment creates no key and starts no generation. Private or older assignments keep their own release and rate.
Try Quest in the Sandbox with your signed-in account. For your application, create a scoped server key in My APIs. Keep it on your backend; browsers call your own authenticated backend. Use a stable pseudonymous X-User-Id, the assigned X-Assignment-Id, and the correct environment’s API host.
Use synthetic data in staging. Education and use involving children require the applicable agreement and safeguards. Your application owns consent, oversight and escalation. Quest is not a grading, diagnosis or guaranteed correctness service.
Prepare without generating
Read GET /v1/quest/catalog for your assignment’s capabilities, limits, write policy and price. Create a session with POST /v1/quest/sessions and a saved Idempotency-Key. Task kinds are homework, practice, exam_prep and open.
{
"setup": {
"task": {
"kind": "practice",
"subject": "Fractions",
"assignment": "Compare fractions using equivalent denominators."
},
"learner": {
"preferred_name": "Sample learner",
"grade_band": "middle",
"language": "en"
},
"team_dialog": false
}
}Save the returned session_id. Setup task and learner preferences are immutable for this session. They do not consume a trial attempt or generation charge. Use a new session for a different task. Responses use US English.
An ordinary first message is learner input. Request a generated opening explicitly with quest.operation: "opening" and an empty prompt. A generated opening uses a turn; do not replace a learner’s actual first message with it.
Choose a posture for each request
| Posture | Purpose |
|---|---|
guided | Help the learner reason toward a next step using questions and hints. |
feedback | Examine the reasoning in learner work supplied with the request. |
explain | Explain a supplied solution or worked method. |
Omitting top-level posture means Guided every time, including after Feedback or Explain. A client can remember its selected posture and send it explicitly. Setup never establishes posture inheritance.
quest.requested_support accepts auto, hint, framework, example or coaching. It requests support without overriding learning safeguards. Optional team_dialog is a boolean override for that turn. Do not send privileged instructions, model settings, replacement memory or raw internal state.
Upload learner work separately
POST /v1/quest/attachments accepts one file as media_type, plain base64 data, optional filename and optional session_id. Save the upload’s idempotency key before dispatch. Use the opaque returned attachment_id in an ordinary turn.
| Input | Limit |
|---|---|
| PNG, JPEG, WebP | 8 MiB per image; 6,000 pixels per side; 36 million pixels |
| UTF-8 text or Markdown | 64 KiB per file |
| Files on a turn | Four handles total |
Files are scoped to workspace, assignment, environment, region and learner. A session-bound upload cannot move to another session. Expiry is fixed at 30 days from upload; reads and reuse do not extend it. Limits are 32 retained uploads and 64 MiB per learner, 256 MiB per workspace/environment, and 100 uploads per learner in a rolling day.
Use legible, relevant work. PDF, SVG, external file URLs and data URLs are not accepted. Handles cannot accompany legacy inline images, opening, closeout or slash commands. Older inline-image clients retain their separate three-image, 8 MiB aggregate bound.
Upload is free of generation charges. GET /v1/quest/attachments/ID requires sessions:read; deletion is idempotent. Export-only retrieval uses POST /v1/quest/data/export/attachments/ID and data:export. Neither route creates a public file URL.
Send a turn and validate its result
Save the following body as saved-turn.json, substituting returned IDs. Omit attachment_ids entirely for a text-only turn. Use one saved operation UUID per new operation. Set the base URL to your assigned host ending in /v1.
{
"session_id": "<returned-session-id>",
"prompt": "Help me check the steps in my work.",
"posture": "feedback",
"quest": {
"operation": "turn",
"requested_support": "auto",
"team_dialog": false,
"attachment_ids": [
"<uploaded-attachment-id>"
]
}
}curl --no-buffer "$FOREVERLEARNING_BASE_URL/chat" \
-H "Authorization: Bearer $FOREVERLEARNING_API_KEY" \
-H "X-Assignment-Id: $FOREVERLEARNING_ASSIGNMENT_ID" \
-H "X-User-Id: synthetic-learner-1" \
-H "FL-API-Version: 2026-12-01" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $SAVED_OPERATION_ID" \
--data-binary @saved-turn.jsonThis cURL snippet shows the request. The runnable examples below also validate completion. Prompts allow up to 10,000 UTF-16 code units. Do not treat HTTP 200 or streamed text as committed success.
- Save the
metadatarequest and session identity. - Render provisional
contentseparately byconversationanddisplaypart, using safe text, Markdown, math and code rendering. - Validate safety status and the closed
quest_receipt. The receipt identifies posture and serving composition; it is not a learning assessment. - Require final
donewith matching session/request identity and the original commercial result. For closeout, also requiresession_ended.
Unknown future events do not replace required completion. Never expose raw internal frames, parse hidden carriers from text or treat an unverified preview as saved output. Team dialog may be displayed only when the verified receipt explicitly permits it. A safety result of unknown is not an all-clear.
Read committed learning records
List paginated sessions with GET /v1/quest/sessions. Read setup and status with GET /v1/quest/sessions/ID, latest state at /state, and saved closeout at /summary. The learner’s profile is GET /v1/quest/learners/USER_ID/profile; its path must match the authenticated learner.
State includes saved progress, posture, effective support when known, observed signals and source turn/time. Profile separates supplied preferences from observed signals and support history. Empty evidence stays null or unavailable. These reads never generate observations, grades, diagnoses or mastery percentages.
Finish through POST /v1/sessions/ID/end with a durable operation_id and matching idempotency key. Successful closeout saves the summary. A prepared session can be discarded without buying an opening. Generic session history and request recovery keep their existing scoped routes.
Recover the original operation
Preserve assignment, learner, session, body and idempotency key after a disconnect. Retry exactly those inputs or read GET /v1/requests/REQUEST_ID. A changed body with the same key conflicts. Do not rotate the key because a connection failed.
in_progress means check again; unavailable is not a confirmed failure or permission for another purchase. A committed result must pass the same receipt checks as a stream. Only a confirmed terminal failure permits an explicit new request. The runtime has one bounded internal retry; it creates no second customer charge or trial unit.
Respect an actual Retry-After header. Resolve authentication and scopes for 401/403, balance or funding for 402, original operation/conflict for 409, and allowance or rate limits for 429. Do not infer free retries with new identities.
Export, delete and erase deliberately
POST /v1/quest/data/export returns paginated sessions, profile and attachment inventory with data:export. Follow every next_cursor; download each retained attachment separately. Generic POST /v1/data/export includes message history. Reads do not extend retention.
Deleting a file removes its bytes while preserving committed financial/turn receipts. Discard removes session content and unshared files. POST /v1/data/erase uses the existing learner-erasure authority and data:erase; preserve its operation identity until completion is confirmed. Financial records required for accounting remain.
The console’s Sandbox user namespace is separate from your API integration’s users. Select the correct namespace for export and erasure. Upload previews and file bytes must not be persisted in browser operation storage. Text retention follows the assigned lifecycle: test content expires after 30 days of inactivity, live content after 180; uploads retain their fixed 30-day expiry.
Price and lifetime trial
The public launch rate is USD $0.05 per successful generated turn, including a generated opening and closeout, with supported attachments included. Failed uncommitted generation has no customer charge. Empty setup, uploads, saved reads, internal retry and replay add no generation charge.
Test access has 20 lifetime attempts per workspace for the stable Quest listing, shared across members and keys. A dispatched failure and generated closeout count; the internal retry and replay do not add a second attempt. Reenrollment or a new release does not reset it. Save an attempt for closeout. Exhaustion never silently enables live charging. My APIs and the original receipt identify the rate for an existing assignment.
Download runnable examples
These use saved intents and environment variables, preserve retry identity and validate final receipts. They cover Guided, Feedback on synthetic work, Explain on a supplied solution, posture changes, omitted-posture Guided, uploads, saved reads, recovery and closeout.
- Setup and walkthrough
- Prepare saved request files
- TypeScript transport (Node 24+)
- Python 3.11+ transport
- cURL transport (with Python validation)
- Browser-to-backend loopback example
Quest OpenAPI · Closed Quest JSON Schema · Shared lifecycle OpenAPI · Shared runtime JSON Schema
Keep sibling OpenAPI files and the schemas directory together when importing relative references. Current implementation contracts do not themselves activate access. Consult the guide linked to your actual assignment for its release-specific capabilities.