Skip to content

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

CodeStatusMeaning
200OKRequest successful
204No ContentSuccessful deletion
400Bad RequestInvalid request (bad state transition, invalid notification, etc.)
401UnauthorizedMissing or invalid authentication
404Not FoundResource doesn't exist or isn't accessible
409ConflictResource conflict (watch already running, duplicate entry, etc.)
422Unprocessable EntityValidation error (missing or invalid fields)
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error
503Service UnavailableDependency unavailable (for example, Clerk client)

Error response format

All errors use FastAPI's standard format:

json
{
  "detail": "Error message"
}

For validation errors (422), FastAPI returns field-level details:

json
{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "search_query"],
      "msg": "Field required",
      "input": {}
    }
  ]
}

Common errors

Authentication errors

Invalid API key

Status: 401 Unauthorized

json
{
  "detail": "Invalid API key"
}

Causes: the key doesn't exist, was revoked, or is malformed.

Missing authorization

Status: 401 Unauthorized

json
{
  "detail": "Authorization header missing"
}

Fix: include the Authorization: Bearer {key} header.

Resource errors

Not found

Status: 404 Not Found

json
{
  "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

json
{
  "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

json
{
  "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

json
{
  "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

json
{
  "detail": "Invalid state transition: paused -> completed"
}

Valid state transitions:

  • activepaused
  • activecompleted
  • completedactive

Invalid notification configuration

Status: 400 Bad Request

json
{
  "detail": "Invalid notification: ..."
}

Duplicate notification types

Status: 400 Bad Request

json
{
  "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

json
{
  "detail": "Username cannot be changed once set. This protects your public task URLs from breaking."
}

Missing username for public watches

Status: 400 Bad Request

json
{
  "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

json
{
  "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

json
{
  "detail": "Clerk client not initialized"
}

Returned when an external dependency (such as Clerk) is unavailable.

Debugging

Check the response status

bash
# 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

  1. Sending schedule in create or update. Schedule isn't user-settable; the agent decides cadence.
  2. Expecting a pagination wrapper on list endpoints. GET /api/v1/tasks and the executions endpoint return bare arrays.
  3. Using GET /api/v1/notifications. This endpoint doesn't exist. Use /api/v1/notifications/sends for send history or /api/v1/tasks/{id}/notifications for per-watch trigger feed.
  4. Trying to access private watches without auth. Returns 404, not 403.

Next steps

Released under the MIT License.