Error Handling
Understand and handle errors returned by the webwhen API.
Naming during the transition
Endpoint paths and JSON shapes still use tasks. The rename to webwhen is a later phase.
HTTP status codes
| Code | Status | Meaning |
|---|---|---|
| 200 | OK | Request successful |
| 204 | No Content | Successful deletion |
| 400 | Bad Request | Invalid request (bad state transition, invalid notification, etc.) |
| 401 | Unauthorized | Missing or invalid authentication |
| 404 | Not Found | Resource doesn't exist or isn't accessible |
| 409 | Conflict | Resource conflict (watch already running, duplicate entry, etc.) |
| 422 | Unprocessable Entity | Validation error (missing or invalid fields) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Dependency unavailable (for example, Clerk client) |
Error response format
All errors use FastAPI's standard format:
{
"detail": "Error message"
}For validation errors (422), FastAPI returns field-level details:
{
"detail": [
{
"type": "missing",
"loc": ["body", "search_query"],
"msg": "Field required",
"input": {}
}
]
}Common errors
Authentication errors
Invalid API key
Status: 401 Unauthorized
{
"detail": "Invalid API key"
}Causes: the key doesn't exist, was revoked, or is malformed.
Missing authorization
Status: 401 Unauthorized
{
"detail": "Authorization header missing"
}Fix: include the Authorization: Bearer {key} header.
Resource errors
Not found
Status: 404 Not Found
{
"detail": "Task not found"
}Returned when a resource doesn't exist or when a non-owner tries to access a private watch. webwhen returns 404 (not 403) for private resources to avoid leaking existence.
Conflict
Status: 409 Conflict
{
"detail": "Task is already running or pending. Use force=true to override."
}Returned when trying to execute a watch that already has a running or pending execution, or when a waitlist email already exists.
Validation errors
Missing required fields
Status: 422 Unprocessable Entity
{
"detail": [
{
"type": "missing",
"loc": ["body", "search_query"],
"msg": "Field required",
"input": {}
}
]
}Required fields for watch creation: search_query.
Invalid field values
Status: 422 Unprocessable Entity
{
"detail": [
{
"type": "enum",
"loc": ["body", "state"],
"msg": "Input should be 'active', 'paused' or 'completed'",
"input": "invalid"
}
]
}Business logic errors
Invalid state transition
Status: 400 Bad Request
{
"detail": "Invalid state transition: paused -> completed"
}Valid state transitions:
active↔pausedactive→completedcompleted→active
Invalid notification configuration
Status: 400 Bad Request
{
"detail": "Invalid notification: ..."
}Duplicate notification types
Status: 400 Bad Request
{
"detail": "Multiple notifications of the same type are not supported. Please provide at most one email and one webhook notification."
}Username already set
Status: 400 Bad Request
{
"detail": "Username cannot be changed once set. This protects your public task URLs from breaking."
}Missing username for public watches
Status: 400 Bad Request
{
"detail": "You must set a username before making tasks public"
}Rate limit errors
Status: 429 Too Many Requests
Rate limits are applied per-IP on public endpoints:
- Public watches listing: 10/minute
- Public watch by ID: 20/minute
- Watch RSS feed: 10/minute
- Waitlist join: 5/minute
Server errors
Internal server error
Status: 500 Internal Server Error
{
"detail": "Failed to change task state: ... Task update rolled back."
}When a state transition fails after other fields were updated, the entire update is rolled back and the error is returned.
Service unavailable
Status: 503 Service Unavailable
{
"detail": "Clerk client not initialized"
}Returned when an external dependency (such as Clerk) is unavailable.
Debugging
Check the response status
# Verbose curl output shows status code and headers
curl -v -X GET https://api.webwhen.ai/api/v1/tasks \
-H "Authorization: Bearer sk_..."Common mistakes
- Sending
schedulein create or update. Schedule isn't user-settable; the agent decides cadence. - Expecting a pagination wrapper on list endpoints.
GET /api/v1/tasksand the executions endpoint return bare arrays. - Using
GET /api/v1/notifications. This endpoint doesn't exist. Use/api/v1/notifications/sendsfor send history or/api/v1/tasks/{id}/notificationsfor per-watch trigger feed. - Trying to access private watches without auth. Returns 404, not 403.
Next steps
- Review Authentication for auth setup
- See the Watches API for endpoint details
- Read the API Overview for the full endpoint listing