KEATH.AIKEATH.AI
  • Home
  • KEATH Public API v1
  • Examples and Test Snippets
  • Migration from the Legacy API
  • 主页
  • KEATH Public API v1
  • 示例与测试代码
  • 从旧接口迁移
  • Accueil
  • API publique KEATH v1
  • Exemples et tests rapides
  • Migration depuis l'ancienne API
  • Inicio
  • API pública KEATH v1
  • Ejemplos y pruebas
  • Migración desde la API anterior
  • الصفحة الرئيسية
  • KEATH Public API v1
  • أمثلة واختبارات سريعة
  • الانتقال من الواجهة القديمة
  • Home
  • KEATH Public API v1
  • Examples and Test Snippets
  • Migration from the Legacy API
  • 主页
  • KEATH Public API v1
  • 示例与测试代码
  • 从旧接口迁移
  • Accueil
  • API publique KEATH v1
  • Exemples et tests rapides
  • Migration depuis l'ancienne API
  • Inicio
  • API pública KEATH v1
  • Ejemplos y pruebas
  • Migración desde la API anterior
  • الصفحة الرئيسية
  • KEATH Public API v1
  • أمثلة واختبارات سريعة
  • الانتقال من الواجهة القديمة
  • KEATH Public API v1
  • Examples and Test Snippets
  • Migration from the Legacy API

KEATH Public API v1

This page is written for integration work, not for legal review.

If you only remember one thing, remember this:

  • Use model_id when you are choosing a marking model.
  • Use assignment_id when you are grading work for an assignment that already exists.
  • Use POST /evaluations/one-pass when you want to grade once without creating an assignment.

The safest first integration is:

  1. GET /credits
  2. GET /models
  3. POST /assignments with a model_id
  4. POST /evaluations with the returned assignment_id
  5. Poll status until SUCCESS, FAILURE, or CANCEL
  6. Fetch the final result

Base URL

https://keath.ai/api/keath/public/v1

All requests use:

  • Header: X-API-Key: kct_your_api_key
  • Response format: standard KEATH JSON response body

API keys are created from the product-side authenticated endpoints:

  • POST /api/keath/public_api/keys/create
  • GET /api/keath/public_api/keys/list
  • POST /api/keath/public_api/keys/revoke

What Changed

This documentation replaces the old X-TOKEN-KEY + /api/open/evaluation/* flow.

The current public API is centered around:

  1. Checking credits and accessible models
  2. Parsing question papers and rubrics from multimodal inputs
  3. Creating marking models and student assignments after preview confirmation
  4. Submitting essay evaluations to an existing assignment by text or file
  5. Rewriting feedback into a different style

Endpoint Overview

EndpointMethodPurpose
/creditsGETCheck current available credits
/modelsGETList marking models the API key owner can access
/modelsPOSTCreate/train a marking model
/assignmentsPOSTCreate a student assignment from an existing model_id
/questions-ingest-previewPOSTParse question papers from PDF/image/url assets
/rubrics-ingest-previewPOSTParse rubric documents from PDF/image/url assets
/evaluationsPOSTSubmit an essay to an existing assignment for grading
/evaluations/one-passPOSTSubmit model id, question, rubrics, and student work in one request
/evaluations/batchPOSTQueue 1-100 one-pass evaluations in one request
/evaluations/:taskId/statusGETPoll lightweight evaluation status
/evaluations/:taskIdGETFetch the evaluation result payload
/evaluations/:taskId/cancelPOSTCancel a queued or in-progress evaluation
/feedback-rewritePOSTRewrite feedback sections into a different style
/uploads/filePOSTUpload one local PDF/image file and receive a reusable public URL

Authentication

Example:

X-API-Key: kct_your_api_key

If the key is missing or invalid, the API returns 401.

This key is the KEATH public API key for your account. Do not send the internal model provider key, such as a Gemini or NewAPI key, to public API endpoints.

Key Scope and Billing

Public API keys currently support:

  • user: access only the key owner's models
  • organization: access models across the organization

Important details:

  • Organization-scoped keys can only be created by an organization admin.
  • Organization-scoped model access remains tied to the key owner's current organization-admin permission.
  • Credits are still charged to the key owner's KEATH account even for organization scope. The current billing mode remains owner_user.

GET /credits

Returns the current usable credits for the API key owner.

Example:

curl "https://keath.ai/api/keath/public/v1/credits" \
  -H "X-API-Key: kct_your_api_key"

GET /models

Returns the marking models available to the API key owner. Use model_id from this response when creating assignments or one-pass evaluations.

Example:

curl "https://keath.ai/api/keath/public/v1/models" \
  -H "X-API-Key: kct_your_api_key"

Typical model item:

{
  "model_id": 930,
  "assignment_name": "O Level Situational Writing Model",
  "assignment_desc": "Reusable marking model",
  "post_status": "ready",
  "creation_method": "custom",
  "total_score": 30
}

POST /questions-ingest-preview

Use this endpoint to understand question sheets before creating an assignment.

Supported inputs:

  • Existing public URLs through assets[]
  • Direct file upload with multipart/form-data
  • Optional specification to target a specific question when a PDF contains multiple questions

JSON mode

{
  "assets": [
    {
      "url": "https://cdn.example.com/question-pack.pdf",
      "mime_type": "application/pdf",
      "name": "question-pack.pdf"
    }
  ],
  "specification": "Only parse Question 2. Ignore the other pages.",
  "assignment_name": "O Level Situational Writing Practice 2",
  "assignment_desc": "Situational writing paper for secondary learners"
}

Multipart mode

The API now accepts direct uploads.

  • Field name: file or files
  • Supported MIME types:
    • application/pdf
    • image/png
    • image/jpeg
    • image/webp
    • image/gif
  • Max files per request: 10
  • Max file size per file: 20 MB

You should send an explicit MIME type when possible, for example ;type=application/pdf. If a client sends application/octet-stream or omits the MIME type, KEATH will try to infer the type from the file extension: .pdf, .png, .jpg, .jpeg, .webp, or .gif.

Example:

curl -X POST "https://keath.ai/api/keath/public/v1/questions-ingest-preview" \
  -H "X-API-Key: kct_your_api_key" \
  -F "files=@./question-pack.pdf;type=application/pdf" \
  -F "files=@./page-2.png;type=image/png" \
  -F "specification=Only parse Question 2 from this upload set." \
  -F "assignment_name=O Level Situational Writing Practice 2"

Mixing uploaded files with existing URLs

When you need both direct uploads and existing public URLs in the same request, send:

  • uploaded files via files
  • URL assets via a JSON-stringified assets field

Example:

curl -X POST "https://keath.ai/api/keath/public/v1/questions-ingest-preview" \
  -H "X-API-Key: kct_your_api_key" \
  -F "files=@./scan-1.jpg;type=image/jpeg" \
  -F 'assets=[{"url":"https://cdn.example.com/appendix.pdf","mime_type":"application/pdf","name":"appendix.pdf"}]' \
  -F "specification=Use the uploaded image as the target question and treat the appendix as supporting context."

POST /rubrics-ingest-preview

Use this endpoint to extract rubric levels before assignment creation.

It supports the same two transport modes:

  • JSON with assets
  • multipart/form-data with direct file uploads

Rubric-specific fields:

  • total_score
  • cum_method: sum or average

JSON example:

{
  "assets": [
    {
      "url": "https://cdn.example.com/rubric.pdf",
      "mime_type": "application/pdf"
    }
  ],
  "specification": "Use the rubric table on page 3 only.",
  "total_score": 30,
  "cum_method": "sum"
}

Multipart example:

curl -X POST "https://keath.ai/api/keath/public/v1/rubrics-ingest-preview" \
  -H "X-API-Key: kct_your_api_key" \
  -F "file=@./rubric.pdf;type=application/pdf" \
  -F "total_score=30" \
  -F "cum_method=sum" \
  -F "specification=Use the rubric on the first page only."

POST /models

Create a reusable marking model after questions and rubrics have been confirmed.

The request body is aligned with the product's internal model creation DTO. In practice this means you should send the final:

  • assignment_name
  • assignment_desc
  • parsed questions
  • parsed rubrics
  • scoring metadata such as total_score and cum_method

Recommended workflow:

  1. Preview questions
  2. Preview rubrics
  3. Let the user review and edit the result
  4. Call POST /models

POST /assignments

Create a student-facing assignment from an existing marking model. This matches the product UI flow: first choose a model in Model Selection, then create the assignment.

Required fields:

  • model_id: a ready model from GET /models
  • assignment_name
  • assignment_desc
  • deadline_time
  • expected_number

Optional fields:

  • project_subject
  • start_time
  • file_type: word, pdf, image, or text; default is pdf

Example:

{
  "model_id": 930,
  "assignment_name": "Situation Writing Test",
  "assignment_desc": "Student-facing writing assignment",
  "project_subject": "English",
  "deadline_time": "2026-06-01T00:00:00.000Z",
  "expected_number": 30,
  "file_type": "pdf"
}

Typical response:

{
  "assignment_id": 1620,
  "model_id": 930,
  "assignment_name": "Situation Writing Test",
  "status": "active"
}

POST /evaluations

Submit one essay to an existing student assignment and start an asynchronous grading task.

Use this endpoint when the assignment already exists in KEATH. The assignment_id is the product assignment/project ID, for example the id in /homeTab/person/assignments/detail?id=1620.

Required fields:

  • assignment_id: an existing assignment ID from POST /assignments or the product assignment detail URL
  • One of:
    • paper_content: the student's essay text
    • an uploaded answer file through multipart/form-data

Optional fields:

  • current_feedbacks: current rubric-level feedback sections you want KEATH to consider when re-grading or adjusting output
  • style: one of Bullet, Short, or Long; default is Bullet
  • student_id: your external anonymous student ID; KEATH passes it through with the task metadata and does not create or update a KEATH student record

Supported answer file uploads:

  • PDF and images: parsed with Gemini multimodal extraction, including relevant visual content descriptions
  • DOCX: extracted server-side
  • TXT and RTF: extracted server-side

Multipart field names accepted for answer files:

  • answer_file
  • answer_files
  • paper_file
  • paper_files
  • response_file
  • response_files
  • file or files

Upload limits:

  • Max files per request: 10
  • Max file size per file: 20 MB

Example:

{
  "assignment_id": 1620,
  "paper_content": "Dear Mrs Tan,\n\nI am writing to request permission to organize a class recycling drive next Friday...",
  "current_feedbacks": [
    {
      "item": "Content",
      "comment": "The response addresses the task but needs a more explicit closing request.",
      "score": 6
    }
  ],
  "style": "Bullet"
}

Multipart example:

curl -X POST "https://keath.ai/api/keath/public/v1/evaluations" \
  -H "X-API-Key: kct_your_api_key" \
  -F "assignment_id=1620" \
  -F "answer_file=@./student-answer.pdf;type=application/pdf" \
  -F "student_id=anon-student-001" \
  -F "style=Bullet"

Typical response payload:

{
  "task_id": "task_123",
  "status": "PENDING",
  "assignment_id": 1620,
  "model_id": 930,
  "billing_mode": "owner_user",
  "scope": "user",
  "credits_charged": 8
}

POST /evaluations/one-pass

Submit a grading request without creating a new assignment. KEATH uses an existing model for scoring, while the request supplies the question, rubrics, and student work for this single evaluation task.

Use this when an integration already knows which KEATH model to use but does not want to create an assignment first.

Required fields:

  • model_id: an existing model ID from GET /models
  • Student work through either paper_content or uploaded answer file(s)

Rubrics can come from one of:

  • rubrics: structured JSON array
  • rubric_text: plain rubric text
  • rubric_file / rubric_files: uploaded rubric files
  • If none is provided, KEATH falls back to the selected model's stored rubrics

Question/context can come from:

  • question_text
  • question_file / question_files
  • assignment_desc
  • If none is provided, KEATH falls back to the selected model's stored description

Supported direct upload formats:

  • Question/rubric PDF or images
  • Question/rubric DOCX, TXT, or RTF
  • Student answer PDF, images, DOCX, TXT, or RTF

Optional fields:

  • assignment_name
  • assignment_desc
  • cum_method: sum or average; defaults to the selected model's setting
  • total_score: rubric score hint
  • specification: parsing instruction such as "only use Question 2"
  • style: Bullet, Short, or Long; default is Bullet
  • student_id: your external anonymous student ID
  • current_feedbacks

Example:

curl -X POST "https://keath.ai/api/keath/public/v1/evaluations/one-pass" \
  -H "X-API-Key: kct_your_api_key" \
  -H "Idempotency-Key: eval-student-001-attempt-1" \
  -F "model_id=930" \
  -F "question_file=@./question.pdf;type=application/pdf" \
  -F "rubric_file=@./rubric.docx;type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
  -F "answer_file=@./student-answer.txt;type=text/plain" \
  -F "student_id=anon-student-001" \
  -F "specification=Use Question 2 only." \
  -F "style=Bullet"

Typical response payload:

{
  "task_id": "public_task_123",
  "status": "QUEUED",
  "stage": "queued",
  "model_id": 930,
  "student_id": "anon-student-001",
  "billing_mode": "owner_user",
  "scope": "user",
  "credits_charged": 0,
  "credit_cost": 8,
  "retryable": false
}

This endpoint returns HTTP 202 Accepted. That means KEATH has safely accepted the work; it does not mean grading is finished. Poll with the returned public task_id.

Use a unique Idempotency-Key for each logical submission. Retrying the same request with the same key returns the same task_id. Reusing that key with different text or files returns 409 instead of creating an accidental duplicate.

Files are stored before the task enters the worker queue. Question, rubric, PDF, image, DOCX, TXT, and RTF processing happens in the queue, so the initial request does not need to remain open for several minutes. Credits are charged only after a complete result is validated. Failed or cancelled tasks keep credits_charged: 0.

POST /evaluations/batch

Submit between 1 and 100 one-pass evaluations as one asynchronous batch. Each item accepts the same fields as /evaluations/one-pass.

curl -X POST "https://keath.ai/api/keath/public/v1/evaluations/batch" \
  -H "X-API-Key: kct_your_api_key" \
  -H "Idempotency-Key: class-5a-writing-2026-07-15" \
  -H "Content-Type: application/json" \
  -d '{
    "evaluations": [
      {
        "model_id": 930,
        "question_text": "Write a letter proposing one school improvement.",
        "paper_content": "Dear Principal, ...",
        "student_id": "anon-student-001",
        "style": "Bullet"
      },
      {
        "model_id": 930,
        "question_text": "Write a letter proposing one school improvement.",
        "paper_content": "Dear Principal, I suggest ...",
        "student_id": "anon-student-002",
        "style": "Bullet"
      }
    ],
    "callback_url": "https://integration.example.com/keath/results"
  }'

The optional callback_url must be a public HTTPS URL. KEATH retries callback delivery. You should still keep status polling as a fallback.

Typical HTTP 202 response:

{
  "task_id": "public_batch_123",
  "status": "QUEUED",
  "stage": "queued",
  "count": 2,
  "callback_url": "https://integration.example.com/keath/results",
  "credits_charged": 0,
  "credit_cost": 16,
  "retryable": false
}

GET /evaluations/:taskId/status

Poll a lightweight task snapshot while the evaluation is still running.

Example:

curl "https://keath.ai/api/keath/public/v1/evaluations/task_123/status" \
  -H "X-API-Key: kct_your_api_key"

Typical fields:

  • task_id
  • status
  • stage
  • position
  • length
  • cur_step_name
  • cur_step
  • total_step
  • progress
  • message

status is the uppercase compatibility value. stage is the clearer lowercase lifecycle value:

  • QUEUED / queued
  • UPLOADING / uploading
  • SUBMITTED / submitted
  • PROGRESS / processing
  • SUCCESS / completed
  • FAILURE / failed
  • CANCEL / cancelled

Poll every 3 seconds at first, then back off to at most every 15 seconds. Stop after a bounded wait and retry later rather than keeping an infinite loop.

Compatibility note: POST /evaluations for an existing assignment still uses the legacy task path. It normally starts at PENDING and charges credits when the task is accepted. The new 202, idempotency, durable queue, and completion-only billing behavior described above applies to /evaluations/one-pass and /evaluations/batch.

GET /evaluations/:taskId

Fetch the evaluation record for a task owned by the API key owner.

Example:

curl "https://keath.ai/api/keath/public/v1/evaluations/task_123" \
  -H "X-API-Key: kct_your_api_key"

The result payload includes:

  • aggregate score
  • rubric-level evaluation items
  • parsed rubrics
  • queue/progress fields when the task is not yet finished

If grading fails, the task status is FAILURE. In that case evaluation may contain an item with type: "error" and a plain error message in comment. Treat that as a failed grading run, not as a score.

Typical successful payload:

{
  "task_id": "task_123",
  "status": "SUCCESS",
  "score": 15,
  "evaluation": [
    {
      "item": "Content",
      "score": 7,
      "feedback": "Relevant ideas are present but the closing request can be stronger."
    },
    {
      "item": "Language",
      "score": 8,
      "feedback": "Language is generally clear with a few minor slips."
    }
  ],
  "rubrics": [
    {
      "item": "Content"
    },
    {
      "item": "Language"
    }
  ]
}

POST /evaluations/:taskId/cancel

Cancel a queued or in-progress evaluation.

Example:

curl -X POST "https://keath.ai/api/keath/public/v1/evaluations/task_123/cancel" \
  -H "X-API-Key: kct_your_api_key"

POST /feedback-rewrite

Rewrite existing feedback into a different format.

Supported styles:

  • bullet_points
  • paragraph
  • short_sentences

You can also:

  • provide a free-text instruction
  • set target_item to rewrite only one criterion

Example:

{
  "style": "bullet_points",
  "instruction": "Make this shorter and more student-friendly.",
  "sections": [
    {
      "item": "Content",
      "comment": "Your answer contains relevant ideas but the explanation remains underdeveloped.",
      "score": 7,
      "max_score": 10
    }
  ]
}

POST /uploads/file

Upload one local PDF/image file and receive a reusable KEATH-hosted URL.

This is optional, but it is useful when:

  • your client cannot expose a temporary public file URL
  • you want to upload once and reuse the returned URL in questions-ingest-preview or rubrics-ingest-preview
  • you are building a CLI or agent integration

Supported file types:

  • application/pdf
  • image/png
  • image/jpeg
  • image/webp
  • image/gif
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document
  • application/rtf
  • text/rtf
  • text/plain

Request shape:

  • Content type: multipart/form-data
  • Field name: file
  • File count: exactly 1 file per request
  • Max file size: 20 MB

As with the preview endpoints, KEATH prefers an explicit MIME type. If your client sends application/octet-stream or omits the MIME type, KEATH will try to infer the type from the file extension when it is one of the supported PDF/image formats.

Example:

curl -X POST "https://keath.ai/api/keath/public/v1/uploads/file" \
  -H "X-API-Key: kct_your_api_key" \
  -F "file=@./question-pack.pdf;type=application/pdf"

Typical response payload:

{
  "name": "question-pack.pdf",
  "mime_type": "application/pdf",
  "size": 183245,
  "path": "keath_prod/public_api/99/1746571871000_question-pack.pdf",
  "url": "https://cdn.keath.ai/keath_prod/public_api/99/1746571871000_question-pack.pdf"
}

Error Notes

Common request errors:

  • 401: missing or invalid X-API-Key
  • 402: insufficient credits
  • 403: no access to the model or organization-scoped key is no longer authorized
  • 404: assignment, model, or evaluation not found
  • 409: model is not ready for evaluation, or the evaluation can no longer be cancelled
  • 400: malformed assets, unsupported file type, invalid request body, or missing required text / uploaded files

For multipart requests, if you send assets, it must be a valid JSON array string. For multipart evaluation requests, if you send rubrics or current_feedbacks, they must be valid JSON array strings.

The multimodal preview endpoints were live-tested with:

  • JSON assets pointing to an uploaded image
  • direct multipart image upload
  • direct multipart question PDF upload
  • direct multipart rubric PDF upload

The public evaluation endpoints were verified against:

  • evaluation creation with an existing assignment
  • evaluation creation from uploaded answer files
  • one-pass evaluation with model id, supplied context, rubrics, and uploaded student work
  • async status polling
  • completed-result fetch
  • cancellation for a pending task
  • organization-scoped model visibility checks

Next Steps

  • Examples and test snippets
  • Migration from the legacy API
  • 中文文档
Last Updated: 7/15/26, 8:35 AM
Contributors: 郭炯韦, PJ
Next
Examples and Test Snippets