POSThttps://api.aihubproduction.com/v1/ielts/writing/task2/grade

Chấm bài Task 2

Chấm bài luận. Cần chủ đề, đề bài và bài làm — không cần ảnh.

Request

FieldTypeBắt buộcMô tả
questionstringBẮT BUỘCĐề bài luận, 1–2.000 ký tự.
topicstringBẮT BUỘCChủ đề thật của bài như education hoặc technology, 1–200 ký tự. Đây là môn chủ đề, không phải loại biểu đồ.
essaystringBẮT BUỘCBài làm Task 2, 1–20.000 ký tự.
languagestringtuỳ chọnNgôn ngữ nhận xét. Hiện chỉ nhận vi; nhận xét luôn trả về bằng tiếng Việt.

Response

Envelope { data, meta } của AIHUB. data khớp hợp đồng chấm bài: overall_band chạy từ 0 đến 9 theo bội số 0.5, criteria luôn có đúng 4 phần tử theo thứ tự cố định (bắt đầu bằng task_response), kèm summary, suggestions, next_steps và annotations trích nguyên văn câu trong bài. meta cho request_id để báo hỗ trợ, service, operation và các số đo thời gian; correlation_id chỉ xuất hiện khi bạn gửi X-Correlation-Id.

Lưu ý

  • topic là bắt buộc và là môn chủ đề, khác với chart_type của Task 1.
  • Task 2 không có image_url hay chart_type; gửi kèm hai trường đó vẫn bị 400 INVALID_REQUEST chứ không bị bỏ qua.
  • Đổi bất kỳ trường nào trong body thì phải dùng một Idempotency-Key mới, nếu không sẽ nhận 409 IDEMPOTENCY_CONFLICT.
  • Body vượt 256 KB trả về 413 PAYLOAD_TOO_LARGE. Trường lạ trong body bị từ chối chứ không bị bỏ qua.
  • Ngân sách 60 giây của gateway là trần của phía server — đặt timeout phía client tối thiểu 90 giây. Bản ghi Idempotency-Key giữ 24 giờ.
# Ba biến dưới là ví dụ — thay bằng giá trị thật của bạn, và chỉ chạy trên server của bạn.
AIHUB_API_KEY="aihub_sk_..."            # API key của Organization, có scope writing.grade
AIHUB_USER_IDENTITY="$SIGNED_USER_ASSERTION"   # Organization có Identity configuration đang hoạt động
# AIHUB_USER_IDENTITY="student_456"     # thay bằng Declared User ID nếu Organization chưa có Identity configuration đang hoạt động

# Gán UUID MỘT LẦN cho mỗi lần chấm, rồi dùng lại nguyên biến này khi retry.
# Chạy lại dòng này = một lần chấm mới, bị tính phí thêm.
AIHUB_IDEMPOTENCY_KEY="$(uuidgen)"

curl -X POST 'https://api.aihubproduction.com/v1/ielts/writing/task2/grade' \
  -H "X-API-Key: $AIHUB_API_KEY" \
  -H "X-User-Identity: $AIHUB_USER_IDENTITY" \
  -H "Idempotency-Key: $AIHUB_IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "question": "Some people believe that universities should only offer academic subjects, while others think vocational training is equally important. Discuss both views and give your own opinion.",
  "topic": "education",
  "essay": "Universities have traditionally focused on academic subjects, yet the value of vocational training is increasingly recognised. An academic education develops the ability to analyse and argue, which serves a society that needs a critical mass of researchers and policy makers. Vocational training, by contrast, turns that analysis into a specific craft, and a society also needs people who can build, repair and maintain. Neither path is complete alone: a trained technician who cannot reason about a problem will repeat the same mistake, and a graduate who cannot make anything will theorise about work they have never done. I therefore consider vocational training a subject a university should offer, provided it carries the same status and the same faculty as academic study."
}'

Lỗi thường gặp

19 mã dưới đây áp dụng cho cả hai endpoint chấm. Mọi lỗi trả về cùng một envelope { error: { code, message, request_id, retryable, retry_after_ms? } }. Giá trị retry_after_ms trong ví dụ chỉ minh họa; hãy dùng giá trị response thực tế. Rẽ nhánh theo code chứ không theo message, vì message có thể đổi câu chữ. Chỉ tự động retry khi retryable là true và luôn giữ cùng Idempotency-Key; riêng 409 do request giống hệt đang chạy thì chờ request đầu hoàn tất rồi gửi lại cùng key và body.

400INVALID_REQUESTRequest không hợp lệ

Body sai kiểu dữ liệu, thiếu trường bắt buộc, quá độ dài, có trường lạ, hoặc Idempotency-Key thiếu/rỗng/quá 255 byte. Sửa request rồi gọi lại; retry y hệt là vô nghĩa.

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
401UNAUTHORIZEDAPI key không hợp lệ

Thiếu, không nhận ra, đã thu hồi hoặc hết hạn API key. Key hiển thị một lần lúc tạo nên cần xoay vòng nếu đã mất.

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
401USER_IDENTITY_REQUIREDThiếu User Identity

Gọi endpoint user-scoped mà không có X-User-Identity. Cả hai endpoint chấm đều cần header này.

{
  "error": {
    "code": "USER_IDENTITY_REQUIRED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
401INVALID_USER_IDENTITYUser Identity không hợp lệ

Declared User ID vi phạm quy tắc 1–256 ký tự ASCII không khoảng trắng, hoặc Signed User Assertion không vượt kiểm chữ ký. Không có fallback: cấu hình Identity active buộc phải dùng Signed User Assertion.

{
  "error": {
    "code": "INVALID_USER_IDENTITY",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
403FORBIDDENThiếu scope

API key không có scope writing.grade. Liên hệ AIHUB để mở rộng scope của key.

{
  "error": {
    "code": "FORBIDDEN",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
403ENVIRONMENT_NOT_ALLOWEDSai Environment

Key chỉ chạy ở Environment khác với hostname đang gọi. Dùng đúng key của host đó; client không chọn Environment bằng header.

{
  "error": {
    "code": "ENVIRONMENT_NOT_ALLOWED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
404NOT_FOUNDSai route

Không có route này. Các operation chấm nằm dưới /v1/ielts/..., ví dụ /v1/writing/task2/grade là 404.

{
  "error": {
    "code": "NOT_FOUND",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
409IDEMPOTENCY_CONFLICTXung đột Idempotency-Key

Dùng lại một key với body khác, hoặc một request giống hệt đang chạy. retryable là false nên không gửi lại ngay; body khác thì lấy key mới, còn request đang chạy thì chờ nó xong rồi gửi lại cùng key và cùng body.

{
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
413PAYLOAD_TOO_LARGEBody vượt 256 KB

Body lớn hơn giới hạn của endpoint. Rút ngắn bài làm.

{
  "error": {
    "code": "PAYLOAD_TOO_LARGE",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
429RATE_LIMITEDVượt rate limit

Quá nhiều request mỗi phút. Nghỉ đúng retry_after_ms rồi thử lại.

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true,
    "retry_after_ms": 2000
  }
}
429CONCURRENCY_LIMITVượt giới hạn song song

Quá nhiều request đang chạy cùng lúc. Giảm số request chạy song song thay vì chờ lâu hơn.

{
  "error": {
    "code": "CONCURRENCY_LIMIT",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true,
    "retry_after_ms": 500
  }
}
429QUOTA_EXCEEDEDHết hạn mức

Đã dùng hết hạn mức request trong tháng. retryable là true và retry_after_ms cho biết thời gian còn lại đến đầu tháng UTC tới; chờ kỳ reset hoặc nâng hạn mức.

{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true,
    "retry_after_ms": 86400000
  }
}
500INTERNAL_ERRORLỗi phía AIHUB

Lỗi không lường trước ở phía chúng tôi. Mã này luôn có retryable là false — báo lại kèm request_id thay vì retry.

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
502AI_SERVICE_ERRORLỗi khi chấm bài

Yêu cầu không hoàn tất. retryable là false với lỗi 4xx (như ví dụ), true với lỗi 5xx; chỉ thử lại khi response thực tế cho phép.

{
  "error": {
    "code": "AI_SERVICE_ERROR",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
502AI_SERVICE_CONTRACT_VIOLATIONKết quả không đúng định dạng

Kết quả trả về có cấu trúc không hợp lệ. Không retry; báo lại kèm request_id.

{
  "error": {
    "code": "AI_SERVICE_CONTRACT_VIOLATION",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": false
  }
}
503AI_SERVICE_THROTTLEDYêu cầu đang quá tải

Lùi rồi thử lại; mã này không có retry_after_ms nên chọn thời gian lùi phù hợp.

{
  "error": {
    "code": "AI_SERVICE_THROTTLED",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true
  }
}
503AI_SERVICE_UNAVAILABLETạm thời chưa thể xử lý

Thử lại với backoff.

{
  "error": {
    "code": "AI_SERVICE_UNAVAILABLE",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true
  }
}
503IDENTITY_PROVIDER_UNAVAILABLENguồn xác minh User Identity không khả dụng

AIHUB không đọc được Identity configuration của Organization, hoặc không lấy được JWKS khi xác minh Signed User Assertion. Lỗi đọc cấu hình có thể xảy ra cả khi bạn dùng Declared User ID; không tự chuyển sang chế độ khác. API key vẫn hợp lệ.

{
  "error": {
    "code": "IDENTITY_PROVIDER_UNAVAILABLE",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true
  }
}
504AI_SERVICE_TIMEOUTHết thời gian chấm

Chấm vượt ngân sách 60 giây. Retry bằng chính Idempotency-Key cũ: nếu lần chấm nền đã xong, kết quả được trả lại mà không tính phí thêm.

{
  "error": {
    "code": "AI_SERVICE_TIMEOUT",
    "message": "…",
    "request_id": "req_01M23NT8MJW22AYX6FVT17F5XF",
    "retryable": true
  }
}
‹ Quay lại Chấm điểm