Appearance
Errors & limits
Error format
Most application errors return JSON:
json
{ "error": "not_found" }Validation failures include Zod details:
json
{
"error": "validation_error",
"details": {
"fieldErrors": {},
"formErrors": []
}
}Error codes
| HTTP | error | Meaning |
|---|---|---|
| 400 | validation_error | Invalid query, body, or path (see details) |
| 401 | missing_credentials | No Bearer token |
| 401 | invalid_credentials | Unknown, revoked, or invalid token |
| 403 | key_not_usable | Key not linked to a partner account with a user |
| 404 | not_found | Grading session or webhook target not found / not owned |
| 429 | (framework) | Rate limit exceeded |
| 5xx | (framework) | Unexpected server error |
Rate limiting
The API enforces 600 requests per minute per instance (global @fastify/rate-limit).
429 responses use the default Fastify rate-limit body (statusCode, error, message), not the { error: "..." } shape above.
Recommended client behavior:
- Exponential backoff on
429and5xx - Honor
Retry-Afterif present - Avoid tight polling loops on list endpoints — use webhooks + single-session fetch instead
CORS
Access-Control-Allow-Origin reflects the request origin. The API is intended for server-to-server use; CORS is permissive because no cookies are used.
Health check
http
GET /healthNo authentication. Returns { "status": "ok" } for load balancers and uptime monitors.