Error Reference
Complete reference of Need2Watch API error codes with HTTP status codes, descriptions, common causes, and solutions.
Error Response Format
All API errors follow this structure:
{
"error": "Human-readable error message",
"code": "ERR_MACHINE_READABLE_CODE",
"message": "Additional context (optional)"
}
Example:
{
"error": "Limit exceeded",
"code": "ERR_LIMIT_EXCEEDED",
"message": "Free tier: 10 monitors maximum. Upgrade at /pricing"
}
Authentication Errors (401)
ERR_UNAUTHORIZED
HTTP Status: 401 Unauthorized
Description: Authentication failed or missing credentials.
Common Causes:
- Missing
Authorizationheader - Missing
X-API-Keyheader - Invalid or expired JWT token
- Invalid API key
- API key revoked
Solutions:
# Option 1: Use API key
curl https://api.need2.watch/v1/monitors \
-H "X-API-Key: n2w_live_xxxxx"
# Option 2: Use JWT bearer token
curl https://api.need2.watch/v1/monitors \
-H "Authorization: Bearer eyJhbGci..."
Example Response:
{
"error": "Invalid credentials",
"code": "ERR_UNAUTHORIZED"
}
ERR_INVALID_REFRESH_TOKEN
HTTP Status: 401 Unauthorized
Description: Refresh token is invalid or expired.
Common Causes:
- Refresh token expired (7 days)
- Token was manually revoked via logout
- Token never existed
Solutions:
- Login again to obtain new tokens
- Use API keys instead (recommended for long-lived access)
Example Response:
{
"error": "Invalid or expired refresh token",
"code": "ERR_INVALID_REFRESH_TOKEN"
}
Validation Errors (400)
ERR_INVALID_REQUEST
HTTP Status: 400 Bad Request
Description: Request validation failed.
Common Causes:
- Missing required fields
- Invalid field types
- Invalid field values
- Malformed JSON
Solutions:
- Check required fields are present
- Verify field types match API spec
- Ensure JSON is valid
Example Response:
{
"error": "Validation failed",
"code": "ERR_INVALID_REQUEST",
"message": "intent is required for nlp mode"
}
ERR_INVALID_EMAIL
HTTP Status: 400 Bad Request
Description: Email format is invalid.
Common Causes:
- Missing @ symbol
- Invalid domain
- Whitespace in email
Solutions:
{
"email": "user@example.com" // ✓ Valid
}
Example Response:
{
"error": "Invalid email format",
"code": "ERR_INVALID_EMAIL"
}
ERR_WEAK_PASSWORD
HTTP Status: 400 Bad Request
Description: Password does not meet security requirements.
Requirements:
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
Solutions:
{
"password": "SecurePassword123!" // ✓ Valid
}
Example Response:
{
"error": "Password must be at least 8 characters and include uppercase, lowercase, and number",
"code": "ERR_WEAK_PASSWORD"
}
ERR_EMAIL_EXISTS
HTTP Status: 400 Bad Request
Description: Email already registered.
Common Causes:
- Attempting to register with existing email
- User already has an account
Solutions:
- Use login endpoint instead:
POST /auth/login - Use password reset if forgotten
- Use different email
Example Response:
{
"error": "Email already registered",
"code": "ERR_EMAIL_EXISTS"
}
Resource Not Found (404)
ERR_NOT_FOUND
HTTP Status: 404 Not Found
Description: Requested resource does not exist.
Common Causes:
- Monitor ID doesn't exist
- Monitor belongs to different user
- Webhook ID doesn't exist
- API key ID doesn't exist
Solutions:
- Verify resource ID is correct
- Check resource belongs to authenticated user
- Use
GET /monitorsto list valid monitor IDs
Example Response:
{
"error": "Monitor not found",
"code": "ERR_NOT_FOUND"
}
Rate Limiting (429)
ERR_RATE_LIMIT
HTTP Status: 429 Too Many Requests
Description: Rate limit exceeded for registration or login attempts.
Limits:
- Registration: 3 attempts per hour per IP
- Login: 5 attempts per 15 minutes per IP
Solutions:
- Wait for rate limit window to reset
- Use API keys for programmatic access (not rate limited)
Example Response:
{
"error": "Too many login attempts",
"code": "ERR_RATE_LIMIT",
"message": "Maximum 5 login attempts per 15 minutes"
}
ERR_LIMIT_EXCEEDED
HTTP Status: 429 Too Many Requests
Description: Account tier limit exceeded.
Free Tier Limits:
- 10 monitors maximum
- 100 API requests per day
- 15 minute minimum check interval
Full Tier Limits:
- Unlimited monitors
- Unlimited API requests
- 1 minute minimum check interval
Solutions:
- Delete unused monitors
- Upgrade to Full tier at need2.watch/pricing
- Wait for daily API request counter to reset (midnight UTC)
Example Response:
{
"error": "Limit exceeded",
"code": "ERR_LIMIT_EXCEEDED",
"message": "Free tier: 10 monitors maximum. Upgrade at /pricing",
"upgradeUrl": "/pricing"
}
Server Errors (500)
ERR_INTERNAL
HTTP Status: 500 Internal Server Error
Description: Unexpected server error.
Common Causes:
- Database connection failure
- External API timeout (LLM, screenshot service)
- Unexpected exception
Solutions:
- Retry the request with exponential backoff
- Check status.need2.watch for service status
- Contact support if issue persists
Example Response:
{
"error": "Internal server error",
"code": "ERR_INTERNAL",
"message": "Database connection timeout"
}
Webhook-Specific Errors
ERR_WEBHOOK_DELIVERY_FAILED
HTTP Status: N/A (logged in delivery logs)
Description: Webhook delivery failed after all retries.
Common Causes:
- Endpoint unreachable
- Endpoint returned non-2xx status
- Connection timeout (30s)
- SSL certificate error
Solutions:
- Verify endpoint is publicly accessible
- Check endpoint returns 200 status
- Ensure HTTPS certificate is valid
- Respond within 30 seconds
Retry Schedule: 15s, 1m, 5m, 30m, 2h, 12h (6 attempts total)
ERR_WEBHOOK_SIGNATURE_INVALID
HTTP Status: 401 Unauthorized (from your endpoint)
Description: Webhook signature verification failed.
Common Causes:
- Using parsed JSON instead of raw body
- Incorrect secret
- Not using HMAC-SHA256
Solutions:
// ✓ Correct: Use raw body
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-need2watch-signature'];
const valid = verifySignature(req.body, signature, secret);
// ...
});
// ✗ Wrong: Using parsed body
app.post('/webhook', express.json(), (req, res) => {
const signature = req.headers['x-need2watch-signature'];
const valid = verifySignature(req.body, signature, secret); // FAILS
// ...
});
Error Handling Best Practices
Retry with Exponential Backoff
async function callApiWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return await response.json();
}
if (response.status === 429) {
// Rate limited - wait and retry
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
if (response.status >= 500) {
// Server error - retry
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Client error (4xx) - don't retry
const error = await response.json();
throw new Error(error.message || error.error);
} catch (err) {
if (attempt === maxRetries - 1) throw err;
}
}
}
Parse Error Responses
async function handleApiError(response) {
const error = await response.json();
switch (error.code) {
case 'ERR_UNAUTHORIZED':
// Refresh token or redirect to login
break;
case 'ERR_LIMIT_EXCEEDED':
// Show upgrade prompt
if (error.upgradeUrl) {
window.location.href = error.upgradeUrl;
}
break;
case 'ERR_RATE_LIMIT':
// Show user-friendly rate limit message
showToast('Too many requests. Please wait a moment.');
break;
case 'ERR_INVALID_REQUEST':
// Show validation errors to user
showValidationError(error.message);
break;
default:
// Generic error handling
console.error('API error:', error);
showToast('An error occurred. Please try again.');
}
}
Log Errors for Debugging
function logApiError(error, context) {
console.error('API Error', {
code: error.code,
message: error.message,
context,
timestamp: new Date().toISOString(),
userId: getCurrentUserId(),
endpoint: context.url,
method: context.method
});
// Send to error tracking service
if (error.code === 'ERR_INTERNAL') {
Sentry.captureException(new Error(error.message), {
tags: { errorCode: error.code },
extra: context
});
}
}
HTTP Status Code Summary
| Status Code | Error Code | Description |
|---|---|---|
| 400 | ERR_INVALID_REQUEST | Request validation failed |
| 400 | ERR_INVALID_EMAIL | Invalid email format |
| 400 | ERR_WEAK_PASSWORD | Password requirements not met |
| 400 | ERR_EMAIL_EXISTS | Email already registered |
| 401 | ERR_UNAUTHORIZED | Authentication failed |
| 401 | ERR_INVALID_REFRESH_TOKEN | Refresh token invalid/expired |
| 404 | ERR_NOT_FOUND | Resource not found |
| 429 | ERR_RATE_LIMIT | Too many requests (temporary) |
| 429 | ERR_LIMIT_EXCEEDED | Account tier limit exceeded |
| 500 | ERR_INTERNAL | Internal server error |
Debugging Tips
Enable Request Logging
const response = await fetch(url, {
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
console.log('Request:', {
url,
method: 'POST',
headers: { 'X-API-Key': apiKey.substring(0, 12) + '...' },
body: data
});
console.log('Response:', {
status: response.status,
statusText: response.statusText,
body: await response.json()
});
Check API Status
Before debugging, verify services are operational:
curl https://api.need2.watch/v1/health
Expected response:
{
"status": "ok",
"timestamp": 1706198400000,
"version": "0.1.0",
"checks": {
"database": true,
"kv": true,
"r2": true
}
}
If status: "degraded", check status.need2.watch for incidents.
Test with curl
Isolate issues by testing with curl:
# Test authentication
curl -v https://api.need2.watch/v1/monitors \
-H "X-API-Key: n2w_live_xxxxx"
# Check response headers
curl -I https://api.need2.watch/v1/health
# Test with invalid key
curl -v https://api.need2.watch/v1/monitors \
-H "X-API-Key: invalid"
Getting Help
If you encounter an error not documented here:
- Check status.need2.watch for service incidents
- Review API Reference for endpoint-specific requirements
- Search GitHub Issues
- Contact support: support@need2.watch
Include in your support request:
- Error code and message
- Request details (endpoint, method, payload)
- Response headers
- Timestamp
- Account email