Error Handling
Here's what can go wrong and how to fix it.
HTTP status codes
These tell you if your request worked or what went wrong.
- Name
200 OK- Description
The request was successful.
- Name
201 Created- Description
A new resource was successfully created (e.g., a new order).
- Name
400 Bad Request- Description
The request was invalid. Check the request body for validation errors.
- Name
401 Unauthorized- Description
Authentication failed. Verify your bearer token is correct.
- Name
403 Forbidden- Description
You don't have permission to access this resource.
- Name
404 Not Found- Description
The requested resource doesn't exist.
- Name
422 Unprocessable Entity- Description
The request was well-formed but contained invalid data.
- Name
429 Too Many Requests- Description
Rate limit exceeded. Slow down your requests.
- Name
500 Internal Server Error- Description
Something went wrong on our end. Try again later.
Error format
All errors look like this:
- Name
success- Type
- boolean
- Description
Always
falsefor error responses.
- Name
message- Type
- string
- Description
A human-readable error message.
- Name
errors- Type
- object|null
- Description
Detailed validation errors, keyed by field name.
Error response structure
{
"success": false,
"message": "The given data was invalid.",
"errors": {
"sender_name": [
"The sender name field is required."
],
"destination_locality": [
"The destination locality field is required."
]
}
}
Common problems
Bad token
401 Unauthorized
Your bearer token is missing or wrong.
Fix it:
- Check the header:
Authorization: Bearer {token} - Make sure the token is correct
- Contact your NextDay administrator if your token has been revoked
401 Unauthorized
{
"success": false,
"message": "Unauthenticated.",
"errors": null
}
Invalid data
400 Bad Request / 422 Unprocessable Entity
Something's wrong with the data you sent.
Fix it:
- Check the
errorsobject to see which fields are wrong - Make sure all required fields are included
- Verify the data types are correct
422 Validation Error
{
"success": false,
"message": "The given data was invalid.",
"errors": {
"weight": [
"The weight must be a number.",
"The weight must be at least 0.1."
],
"service_id": [
"The selected service is not available for this route."
]
}
}
Order not found
404 Not Found
Can't find what you're looking for.
Fix it:
- Double-check the tracking number
- Make sure the order belongs to your account
404 Not Found
{
"success": false,
"message": "Order not found.",
"errors": null
}
Too many requests
429 Too Many Requests
You're calling the API too fast.
Fix it:
- Slow down your requests
- Check the
Retry-Afterheader to see when to try again - Cache data that doesn't change often
429 Rate Limited
{
"success": false,
"message": "Too many requests. Please try again later.",
"errors": null
}
Response Headers
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Business rules
422 Unprocessable Entity
The request is valid, but you can't do that action right now.
Common reasons:
- Trying to cancel an order that's already delivered or nulled
- We don't deliver to that location
- Your account has restrictions
Business Logic Error
{
"success": false,
"message": "Order cannot be cancelled after pickup.",
"errors": {
"order": [
"Order is already in transit and cannot be cancelled."
]
}
}
Handle errors properly
1. Check every response
const response = await fetch('/api/v1/orders/create', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(orderData),
})
const data = await response.json()
if (!data.success) {
// Handle error
console.error('Error:', data.message)
if (data.errors) {
Object.entries(data.errors).forEach(([field, messages]) => {
console.error(` ${field}: ${messages.join(', ')}`)
})
}
return
}
// Success - process data.data
2. Retry when it makes sense
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60
await sleep(retryAfter * 1000)
continue
}
if (response.status >= 500 && attempt < maxRetries) {
await sleep(Math.pow(2, attempt) * 1000) // Exponential backoff
continue
}
return response
}
throw new Error('Max retries exceeded')
}
3. Log errors so you can debug
if (!data.success) {
// Log full error details
console.error('API Error:', {
status: response.status,
message: data.message,
errors: data.errors,
requestId: response.headers.get('X-Request-Id'),
timestamp: new Date().toISOString(),
})
}