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 false for 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 errors object 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-After header 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(),
  })
}

Was this page helpful?