Error Responses
Form-specific errors use the RFC 9457 (Problem Details) format with Content-Type: application/problem+json. These include a machine-readable type URI, a short title, the HTTP status code, and an optional human-readable detail message.
All other errors (validation, authentication, rate limiting, etc.) use Laravel's standard JSON error format.
Problem Details Fields
| Field | Type | Description |
|---|---|---|
type |
string |
A URI that identifies the error type. Points to this documentation page. |
title |
string |
A short, human-readable summary of the error type. |
status |
integer |
The HTTP status code. |
detail |
string |
A human-readable explanation specific to this occurrence (optional). |
Form-Specific Errors (Problem Details)
Form Inactive {#form-inactive}
Returned when the form is not accepting submissions.
{
"type": "https://headless-form.robertboes.nl/docs/api-errors#form-inactive",
"title": "Form Inactive",
"status": 403,
"detail": "This form is currently inactive."
}
Origin Not Allowed {#origin-not-allowed}
Returned when the request's Origin header doesn't match any of the form's allowed origins.
{
"type": "https://headless-form.robertboes.nl/docs/api-errors#origin-not-allowed",
"title": "Origin Not Allowed",
"status": 403,
"detail": "The request origin is not in this form's allowed origins list."
}
Origin Required {#origin-required}
Returned when strict origin enforcement is enabled and no Origin header is present.
{
"type": "https://headless-form.robertboes.nl/docs/api-errors#origin-required",
"title": "Origin Required",
"status": 403,
"detail": "The Origin header is required for this form."
}
Spam Detected {#honeypot-detected}
Returned when the honeypot field contains a value (indicating a bot).
{
"type": "https://headless-form.robertboes.nl/docs/api-errors#honeypot-detected",
"title": "Spam Detected",
"status": 422,
"detail": "Bot protection verification failed."
}
Bot Protection Failed {#bot-protection-failed}
Returned when the bot protection challenge token is invalid or missing.
{
"type": "https://headless-form.robertboes.nl/docs/api-errors#bot-protection-failed",
"title": "Bot Protection Failed",
"status": 422,
"detail": "Bot protection verification failed."
}
Standard Errors
These errors use Laravel's default JSON format with a message field.
Validation Error
Returned when submitted data fails validation. Includes field-level error details.
{
"message": "The email field must be a valid email address.",
"errors": {
"email": [
"The email field must be a valid email address."
],
"name": [
"The name field is required."
]
}
}
Too Many Requests
Returned when the rate limit is exceeded. Includes a Retry-After header with the number of seconds to wait.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"message": "Too Many Attempts."
}
Unauthorized
Returned when authentication is required but missing or invalid.
{
"message": "Unauthenticated."
}
Forbidden
Returned when the authenticated user does not have permission to perform the action.
{
"message": "Token does not have the required ability."
}
Not Found
Returned when the requested resource does not exist.
{
"message": "Not Found."
}
Error Handling Example
const response = await fetch('https://headless-form.robertboes.nl/api/v1/form/01JCK...', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '', email: 'invalid' }),
});
if (!response.ok) {
const error = await response.json();
if (error.type) {
// Problem Details error (form-specific)
console.error(`${error.title}: ${error.detail ?? ''}`);
} else if (response.status === 422 && error.errors) {
// Validation error
for (const [field, messages] of Object.entries(error.errors)) {
console.error(`${field}: ${messages.join(', ')}`);
}
} else if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
console.error(`Rate limited. Try again in ${retryAfter} seconds.`);
} else {
console.error(error.message);
}
}