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:
{
"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:
{
"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
{
"error": "invalid_token",
"error_description": "The access token is invalid or has expired"
}
What to do: Refresh your access token and retry.
403 Forbidden
{
"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
{
"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
{
"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
{
"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
{
"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:
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:
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
Client errors (don't retry without fixing)
Rate limit
Server errors (always retry)
Testing Status Codes
Test how your app handles each status:
// 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
// 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
// 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
// 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();
}