Skip to main content

Overview

The Quiz Quail API uses RFC 9457 Problem Details for all error responses. Every error response has Content-Type: application/problem+json and a consistent JSON structure.

Error Format

{
  "type": "https://quizquail.com/problems/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Quiz 'quiz_abc123' not found"
}
FieldTypeDescription
typestringA URI identifying the error type. Stable — safe to match on.
titlestringShort human-readable summary of the error type.
statusnumberHTTP status code.
detailstringHuman-readable explanation specific to this occurrence.
instancestring(Optional) URI identifying this specific error occurrence.
errorsarray(Optional) Field-level validation errors. See below.

Error Types

400 — Validation Error

Returned when the request body or query parameters fail validation.
{
  "type": "https://quizquail.com/problems/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "errors": [
    { "field": "title", "message": "Required", "code": "invalid_type" },
    { "field": "rounds", "message": "Array must contain at least 1 element(s)", "code": "too_small" }
  ]
}
The errors array contains per-field details:
FieldTypeDescription
fieldstringDot-separated path to the invalid field.
messagestringHuman-readable description of the problem.
codestring(Optional) Machine-readable error code.

401 — Unauthorized

Returned when the request is missing or has an invalid API key.
{
  "type": "https://quizquail.com/problems/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required"
}

403 — Forbidden

Returned when the API key does not have the required scope for the operation.
{
  "type": "https://quizquail.com/problems/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "You do not have permission to perform this action"
}

404 — Not Found

Returned when the requested resource does not exist or is not accessible to your account.
{
  "type": "https://quizquail.com/problems/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Quiz 'quiz_abc123' not found"
}

409 — Conflict

Returned when the request conflicts with the current state of a resource (e.g., trying to render a quiz that is already rendering).
{
  "type": "https://quizquail.com/problems/conflict",
  "title": "Conflict",
  "status": 409,
  "detail": "A render is already in progress for this quiz"
}

429 — Too Many Requests

Returned when you exceed the rate limit for the endpoint. Includes a Retry-After header.
{
  "type": "https://quizquail.com/problems/rate-limit-exceeded",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Try again in 12 seconds."
}

500 — Internal Server Error

Returned when an unexpected error occurs. The detail field will not contain sensitive information.
{
  "type": "https://quizquail.com/problems/internal-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred"
}

Handling Errors

Match on type, Not title

The type URI is stable and safe to use in error-handling logic. The title and detail fields are human-readable and may change.
async function createQuiz(apiKey, quizData) {
  const response = await fetch("https://app.quiz-quail.com/api/v1/quizzes", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(quizData),
  });

  if (!response.ok) {
    const error = await response.json();

    switch (error.type) {
      case "https://quizquail.com/problems/validation-error":
        console.error("Validation failed:", error.errors);
        break;
      case "https://quizquail.com/problems/rate-limit-exceeded":
        const retryAfter = response.headers.get("Retry-After");
        console.error(`Rate limited. Retry in ${retryAfter}s`);
        break;
      case "https://quizquail.com/problems/unauthorized":
        console.error("Check your API key");
        break;
      default:
        console.error(`API error: ${error.detail}`);
    }

    throw new Error(error.detail);
  }

  return response.json();
}

Display Validation Errors

For 400 responses with field-level errors, map them to your form fields:
if (error.type === "https://quizquail.com/problems/validation-error") {
  for (const fieldError of error.errors) {
    console.error(`${fieldError.field}: ${fieldError.message}`);
    // e.g. "title: Required"
    // e.g. "rounds: Array must contain at least 1 element(s)"
  }
}