HTTP Status Codes

Understanding API response codes and error formats in 5 minutes

5 mins
Beginner

The DoorFlow API uses standard HTTP status codes to indicate success or failure. Learn what each code means and how to handle them.

Success Codes (2xx)

These indicate your request succeeded:

Code Status Meaning When You See It
200 OK Request succeeded, response contains data GET, PUT requests
201 Created Resource created successfully POST requests
204 No Content Request succeeded, no response body DELETE requests

Example 200 response:

json
{
  "id": 12345,
  "first_name": "Jane",
  "last_name": "Smith",
  "email": "jane@example.com",
  "enabled": true
}

Example 204 response: No body - just check response.status === 204 to confirm success.

Client Error Codes (4xx)

These indicate something was wrong with your request:

Code Status Meaning Your Action
400 Bad Request Malformed request syntax Fix request format/structure
401 Unauthorized Invalid/expired token Refresh token or re-authenticate
403 Forbidden Insufficient permissions Check OAuth scopes
404 Not Found Resource doesn't exist Verify resource ID
422 Unprocessable Entity Validation error Fix request data
429 Too Many Requests Rate limit exceeded Wait and retry

4xx = Your Fault

These errors mean your request needs to be fixed. Don't retry without changing something.

Server Error Codes (5xx)

These indicate a problem on DoorFlow's side:

Code Status Meaning Your Action
500 Internal Server Error Server encountered an error Retry with backoff
502 Bad Gateway Upstream service error Retry with backoff
503 Service Unavailable Temporary outage Retry with backoff
504 Gateway Timeout Request took too long Retry with backoff

5xx = DoorFlow's Fault

These errors are temporary. Always retry with exponential backoff.

Error Response Format

All errors return JSON with this structure:

json
{
  "error": "error_code",
  "error_description": "Human-readable description",
  "errors": {
    "field_name": ["Specific field error"]
  }
}

Fields

error - Machine-readable error code:

  • invalid_token
  • not_found
  • validation_failed
  • rate_limit_exceeded
  • insufficient_scope

error_description - Human-readable message:

  • Show to developers
  • Don't show directly to end users (may contain technical details)

errors - Field-specific validation errors (422 only):

  • Object mapping field names to error arrays
  • Only present for validation failures

Example Error Responses

401 Unauthorized

json
{
  "error": "invalid_token",
  "error_description": "The access token is invalid or has expired"
}

What to do: Refresh your access token and retry.

403 Forbidden

json
{
  "error": "insufficient_scope",
  "error_description": "The access token does not have sufficient scope to perform this action"
}

What to do: Check your OAuth app has required scopes. Update scopes and have users re-authorize.

404 Not Found

json
{
  "error": "not_found",
  "error_description": "The requested resource was not found"
}

What to do: Verify the resource ID is correct. Resource may have been deleted.

422 Validation Error

json
{
  "error": "validation_failed",
  "error_description": "Validation failed",
  "errors": {
    "email": ["has already been taken"],
    "first_name": ["can't be blank"],
    "card_number": ["is invalid"]
  }
}

What to do: Fix the invalid fields and retry. Check the errors object for specific field problems.

429 Rate Limit

json
{
  "error": "rate_limit_exceeded",
  "error_description": "API rate limit exceeded. Maximum 120 requests per minute."
}

What to do: Wait until rate limit resets (check X-RateLimit-Reset header) and retry.

500 Server Error

json
{
  "error": "internal_server_error",
  "error_description": "An internal server error occurred"
}

What to do: Retry with exponential backoff. If persists, contact support.

Rate Limit Headers

Every response includes rate limit information:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1704070800

Fields

X-RateLimit-Limit - Maximum requests per minute (120)
X-RateLimit-Remaining - Requests left this hour
X-RateLimit-Reset - Unix timestamp when limit resets

Check these headers:

javascript
function checkRateLimit(response) {
  const remaining = response.headers.get('X-RateLimit-Remaining');
  const reset = response.headers.get('X-RateLimit-Reset');

  console.log(`Rate limit: ${remaining} requests remaining`);

  if (parseInt(remaining) < 50) {
    console.warn('Warning: Low rate limit remaining!');
    console.log(`Resets at: ${new Date(reset * 1000).toLocaleString()}`);
  }
}

Basic Error Handling

Check status code first, then parse error:

javascript
async function callAPI(url, options) {
  const response = await fetch(url, options);

  // Check if successful (2xx status)
  if (response.ok) {
    // Success - return data
    if (response.status === 204) {
      return null; // No content
    }
    return await response.json();
  }

  // Error - parse error response
  const error = await response.json();

  console.error('API Error:', {
    status: response.status,
    error: error.error,
    description: error.error_description,
  });

  // Handle specific status codes
  if (response.status === 401) {
    // Token expired - refresh and retry
    console.log('Token expired');
  } else if (response.status === 422) {
    // Validation error - show field errors
    console.error('Validation errors:', error.errors);
  } else if (response.status >= 500) {
    // Server error - retry
    console.log('Server error - will retry');
  }

  throw new Error(`API error: ${error.error_description}`);
}

Quick Decision Tree

Status code?
├─ 2xx → Success! Parse response
├─ 401 → Refresh token, retry once
├─ 403 → Check OAuth scopes, fix and retry
├─ 404 → Resource not found, handle gracefully
├─ 422 → Fix validation errors, retry
├─ 429 → Wait until reset, retry
└─ 5xx → Retry with exponential backoff

Status Code Summary

Successful

200, 201, 204 → Parse response, done

Client errors (don't retry without fixing)

400 → Fix request format
401 → Refresh token, then retry
403 → Add missing OAuth scope
404 → Resource doesn't exist
422 → Fix validation errors

Rate limit

429 → Wait and retry

Server errors (always retry)

500, 502, 503, 504 → Exponential backoff

Testing Status Codes

Test how your app handles each status:

javascript
// Test success
const person = await createPerson({ first_name: 'Jane', ... });
console.log('Success:', person); // 201 Created

// Test 404
try {
  await getPerson(99999);
} catch (error) {
  console.log('Handled 404:', error.message);
}

// Test 422
try {
  await createPerson({ /* missing required fields */ });
} catch (error) {
  console.log('Handled 422:', error.message);
}

Common Mistakes

Mistake: Not checking response.ok

javascript
// Bad: Assumes success
const data = await response.json();

// Good: Check status first
if (!response.ok) {
  const error = await response.json();
  throw new Error(error.error_description);
}
const data = await response.json();

Mistake: Retrying 4xx errors

javascript
// Bad: Retrying 404 won't help
if (response.status === 404) {
  await retry(); // Still 404!
}

// Good: Handle 404 gracefully
if (response.status === 404) {
  console.log('Resource not found');
  return null;
}

Mistake: Not retrying 5xx errors

javascript
// Bad: Give up on first 500
if (response.status === 500) {
  throw new Error('Server error');
}

// Good: Retry with backoff
if (response.status === 500) {
  await retryWithBackoff();
}

Next Steps

Learn about specific errors

[Common Errors] - Troubleshooting guide for each error type
[Retry Strategies] - Production-ready retry logic

See in context

[Common Workflows] - Error handling in real workflows
[OAuth Token Refresh] - Handling 401 errors