Error Response Format
All API errors follow a standard envelope:| Field | Type | Always present? | Description |
|---|---|---|---|
success | boolean | ✅ | Always false for errors |
error.code | string | ✅ | Machine-readable code (use for switch/case) |
error.message | string | ✅ | Human-readable description |
error.errors | object | ❌ Only on 422 | Field-level validation errors |
Handling in Dart
Error Code Reference
| Code | HTTP Status | When | Frontend Action |
|---|---|---|---|
UNAUTHORIZED | 401 | Missing/invalid/expired JWT | Refresh token or redirect to login |
FORBIDDEN | 403 | Insufficient role permissions | Show “Access denied” |
ROLE_CONFLICT | 403 | User exists with different role | Show “Already registered as “ |
NOT_FOUND | 404 | Resource doesn’t exist | Show 404 screen |
CONFLICT | 409 | Job already taken by another mechanic | Dismiss offer, show “Already taken” |
INVALID_TRANSITION | 409 | Invalid job status change | Refresh job status from API |
VALIDATION_ERROR | 422 | Request body fails Zod validation | Highlight invalid form fields |
RATING_WINDOW_EXPIRED | 422 | Rating submitted after 7-day window | Show “Rating period expired” |
PAYMENT_REQUIRED | 402 | Unpaid cancellation fees | Redirect to pay fees |
RATE_LIMIT_EXCEEDED | 429 | Too many requests | Show “Try again in X seconds” |
INTERNAL_ERROR | 500 | Server error | Show generic error, retry later |
Rate Limiting
The API enforces rate limits at multiple levels. When exceeded, you’ll receive a 429 response.Limits
| Scope | Limit | Applied to |
|---|---|---|
| Global | 60 requests/minute per IP | All endpoints |
| Auth | 10 requests/minute per IP | /auth/verify-otp only |
| Tracking HTTP | 1 request/2 seconds | GET /tracking/jobs/{id} |
| Admin invite | Stricter limit | /auth/complete-admin-invite |
Socket.io Event Limits
| Event | Limit |
|---|---|
location:update | 60/minute |
job:join | 10/minute |
job:leave | 10/minute |
Response Headers
Every response includes rate limit info:| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests in the window |
X-RateLimit-Remaining | Requests left in current window |
X-RateLimit-Reset | Unix timestamp when window resets |
Handling 429 in Dart
Pagination
Paginated endpoints acceptpage and limit query parameters and return a standard envelope:
| Field | Type | Description |
|---|---|---|
data | array | The items for the current page |
page | number | Current page (1-indexed) |
limit | number | Items per page (default 20) |
total | number | Total items matching the query |
Paginated Endpoints
GET /api/v1/jobs— filter bystatusGET /api/v1/notifications— user notificationsGET /api/v1/admin/users— filter byroleGET /api/v1/admin/mechanics— mechanic listGET /api/v1/admin/jobs— filter bystatusGET /api/v1/ratings/mechanic/{id}— mechanic ratings
Dart Helper
Validation Errors (422)
When request validation fails (Zod), theerrors field contains field-level details: