edutap.ai developers
Exam Feedback and Weakness Reports

API Reference

REST API for exam grading requests and result retrieval

REST API for submitting exam responses to receive per-question AI feedback and weakness reports per attempt.

Common

Base URL

https://tap-file-upload-production.coxwave.link

Authentication

All API requests require the TAP-API-KEY header.

Request Headers

ParameterTypeRequiredDescription
TAP-API-KEYStringOYour issued API key

Question Types

Questions are identified by problem_type. The section (Writing or Speaking) is derived from this value, so you do not send it.

problem_typeQuestionSectionQuestion no.QNOTYPE from the lookup API
MSOMBuild a SentenceWriting1-10MSOM
WDSWrite an EmailWriting11WDS
WDSWrite for an Academic DiscussionWriting12WDS
LISTENREPEATListen and RepeatSpeaking1-7AUDO
INTERVIEWTake an InterviewSpeaking8-11AUDO

Speaking cannot use QNOTYPE as it comes: the lookup API returns AUDO for every speaking question. Split it by question number and send either LISTENREPEAT (1-7) or INTERVIEW (8-11).

A request may only carry questions from one section. Mixing Writing and Speaking questions is rejected with 422.

Question Information

The prompt, task situation and correct answer are not sent with the request. The server fetches them from the question lookup API. A request carries only what identifies the question and the learner's response.

FieldRole
exam_idThe COURSE_ID from the question lookup API, shaped {TEST_ID}_{TESTNO}
question_numberSEQ from the question list, not QNOSEQ which restarts per section

A question is kept once fetched, so the same question is never looked up twice.

Writing is split into sections, so question 11 (Write an Email) carries QNOSEQ 1. Send SEQ, which counts across the whole exam, as question_number.

Feedback Status

statusDescription
PENDINGAccepted, waiting to be graded
PROCESSINGFeedback in progress
DONEGraded, result available
FAILEDFeedback failed, reason in explanation

Exam Feedback

Submit one whole section of an attempt.
One job is created per question, and results are retrieved by the returned job_id.


Submit Writing

POST/api/v1/courses/exam-gradings/writing#
Submits Writing responses for grading.

Request

Request Body

ParameterTypeRequiredDescription
exam_idStringOExam ID. Send the COURSE_ID from the question lookup API as it is (e.g. "I100000760_1")
user_idStringOLearner ID
section_scoreNumberOSection score (1.0-6.0, in steps of 0.5)
itemsArray of objectsOQuestions, at least one. See items item
items item
ParameterTypeRequiredDescription
problem_idStringOQuestion ID. Must be unique within a request
problem_typeStringOMSOM or WDS
question_numberNumberOQuestion number. It decides which rubric a WDS item uses, 11 (Write an Email) or 12 (Academic Discussion). Rejected with 422 when missing
scoreNumberOThis question's score. Build a Sentence is 0 or 1, the rest are 0-5
user_answerStringOThe learner's response

Build a Sentence (MSOM) is graded by exact match against correct_answer. The correct sentence comes from the question lookup, case and punctuation are ignored, and the model only writes the explanation.

Request Example

curl -X POST "https://tap-file-upload-production.coxwave.link/api/v1/courses/exam-gradings/writing" \
  -H "Content-Type: application/json" \
  -H "TAP-API-KEY: {YOUR_API_KEY}" \
  -d '{
    "exam_id": "I100000760_1",
    "user_id": "u-1001",
    "section_score": 4.0,
    "items": [
      {
        "problem_id": "w1",
        "question_number": 1,
        "problem_type": "MSOM",
        "user_answer": "I wonder where did she find the book."
      },
      {
        "problem_id": "w11",
        "question_number": 11,
        "problem_type": "WDS",
        "user_answer": "Dear Professor Kim, thank you for your email about the group project..."
      },
      {
        "problem_id": "w12",
        "question_number": 12,
        "problem_type": "WDS",
        "user_answer": "I agree with Andrew that online shopping has changed local businesses..."
      }
    ]
  }'

Response

202 Accepted

Response Fields

ParameterTypeRequiredDescription
jobsArray of objectsOFeedback jobs created, one per question
jobs[].job_idStringOFeedback job ID, used to retrieve the result
jobs[].problem_idStringOQuestion ID
jobs[].statusStringOStatus at submission (PENDING)

Response Example

{
  "jobs": [
    { "job_id": "6c4e8c77-...", "problem_id": "w1", "status": "PENDING" },
    { "job_id": "5e0b1c2f-...", "problem_id": "w11", "status": "PENDING" },
    { "job_id": "a1b2c3d4-...", "problem_id": "w12", "status": "PENDING" }
  ]
}

Submit Speaking

POST/api/v1/courses/exam-gradings/speaking#
Submits Speaking responses for grading.

Request

Sent as multipart/form-data.

ParameterTypeRequiredDescription
exam_idStringOExam ID. Send the COURSE_ID from the question lookup API as it is (e.g. "I100000760_1")
user_idStringOLearner ID
section_scoreNumberOSection score (1.0-6.0, in steps of 0.5)
itemsStringOQuestions as a JSON array, sent as a string. See items item
audiosArray of filesOAnswer recordings. Names must match audio_file_name in items
items item
ParameterTypeRequiredDescription
problem_idStringOQuestion ID. Must be unique within a request
problem_typeStringOLISTENREPEAT or INTERVIEW
question_numberNumberOQuestion number. Rejected with 422 when missing
scoreNumberOThis question's score. Build a Sentence is 0 or 1, the rest are 0-5
audio_file_nameStringORecording file name for this question. Must exactly match a file uploaded in audios

Recordings are stored and transcribed before grading. The transcript carries the timing of each utterance, so pace and pauses are reflected in the score.

Send the sentence to repeat for Listen and Repeat, and the full question for Take an Interview, from the question lookup. When the lookup fails, there is no way to know what was asked, so the grading ends as FAILED.

Request Example

curl -X POST "https://tap-file-upload-production.coxwave.link/api/v1/courses/exam-gradings/speaking" \
  -H "TAP-API-KEY: {YOUR_API_KEY}" \
  -F "exam_id=I100000760_1" \
  -F "user_id=u-1001" \
  -F "section_score=2.0" \
  -F 'items=[
    {
      "problem_id": "s1",
      "question_number": 1,
      "problem_type": "LISTENREPEAT",
      "score": 5,
      "audio_file_name": "q01.mp3"
    },
    {
      "problem_id": "s8",
      "question_number": 8,
      "problem_type": "INTERVIEW",
      "score": 3,
      "audio_file_name": "q08.mp3"
    }
  ]' \
  -F "audios=@q01.mp3;type=audio/mpeg" \
  -F "audios=@q08.mp3;type=audio/mpeg"

Response

202 Accepted, in the same shape as the Writing request.

{
  "jobs": [
    { "job_id": "7a577745-...", "problem_id": "s1", "status": "PENDING" },
    { "job_id": "b2c3d4e5-...", "problem_id": "s8", "status": "PENDING" }
  ]
}

Retrieve Result

GET/api/v1/courses/exam-gradings/{job_id}#
Retrieves the grading result for a single question.

Request

Path Parameters

ParameterTypeRequiredDescription
job_idStringOFeedback job ID from the submission response

Request Example

curl "https://tap-file-upload-production.coxwave.link/api/v1/courses/exam-gradings/7a577745-8fb2-453d-aa57-b17fc80e0e39" \
  -H "TAP-API-KEY: {YOUR_API_KEY}"

Response

ParameterTypeRequiredDescription
job_idStringOFeedback job ID
statusStringOPENDING / PROCESSING / DONE / FAILED
resultObject-Feedback result, null until the feedback is written
weakness_reportStringOWhether the weakness diagnosis is ready. PENDING or READY
result object
ParameterTypeRequiredDescription
exam_idStringOExam ID
problem_idStringOQuestion ID
question_numberNumberOQuestion number. Rejected with 422 when missing
problem_typeStringOQuestion type
grade_typeStringOWRITING or SPEAKING
scoreNumberOThis question's score. Build a Sentence is 0 or 1, the rest are 0-5
score_baseNumberOMaximum score. 1 for Build a Sentence, 5 otherwise
section_scoreNumberOSection score (1.0-6.0, in steps of 0.5)
explanationStringOFeedback and the reasoning behind the score
user_answerStringOThe learner's response. Transcribed text for Speaking
correct_answerStringOModel answer or correct sentence
assessment_results.strengthArray of stringsOWhat the learner did well
assessment_results.weaknessArray of objectsOWeaknesses, with tag and description
tipsArray of objectsOPractice tips, with description and example
ideaArray of objectsOIdea suggestions

Response Example

{
  "job_id": "7a577745-8fb2-453d-aa57-b17fc80e0e39",
  "status": "DONE",
  "result": {
    "exam_id": "I100000760_1",
    "problem_id": "s1",
    "question_number": 1,
    "problem_type": "LISTENREPEAT",
    "grade_type": "SPEAKING",
    "score": 3.0,
    "score_base": 5.0,
    "section_score": 2.0,
    "explanation": "제시된 문장의 핵심 어휘는 전달했으나 중간에 끊김이 있어 3점입니다.",
    "user_answer": "The ticket counter is over there.",
    "assessment_results": {
      "strength": ["핵심 어휘를 정확히 발화했습니다."],
      "weakness": [
        { "tag": "답변 마무리", "description": "문장 끝을 흐리지 않고 끝까지 발화하세요." }
      ]
    },
    "tips": [
      { "description": "문장을 한 호흡에 말하는 연습을 해보세요.", "example": "The ticket counter / is over there." }
    ],
    "idea": []
  },
  "weakness_report": "READY"
}

Feedback is written in Korean, since it is shown to Korean learners.


Errors

CodeCondition
401Missing or invalid TAP-API-KEY header
404Result requested for an unknown job_id
422items is empty
422Duplicate problem_id in items
422Questions from more than one section in a request
422Unknown problem_type
422A WDS question has no question_number
422No uploaded file matches audio_file_name
422An uploaded audio file is empty