Error Handling
This guide explains how to handle errors from the MenoTime API. Understanding error responses helps you troubleshoot issues and implement robust error handling in your application.
Standard Error Response Format
All API errors follow a consistent JSON structure:
{
"status": "error",
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"additional": "information"
}
}
Error Response Fields
| Field | Type | Description |
|---|---|---|
| status | string | Always "error" for error responses |
| code | string | Machine-readable error code (e.g., VALIDATION_ERROR) |
| message | string | Human-readable error message |
| details | object | Additional context-specific error information |
HTTP Status Codes
2xx - Success
| Code | Meaning |
|---|---|
| 200 | OK - Request succeeded, response body contains result |
| 201 | Created - Resource successfully created |
| 202 | Accepted - Request accepted for processing (async operations) |
| 204 | No Content - Request succeeded, no response body |
3xx - Redirection
| Code | Meaning |
|---|---|
| 301 | Moved Permanently - Resource moved to new location |
| 302 | Found - Temporary redirect |
4xx - Client Error
| Code | Meaning |
|---|---|
| 400 | Bad Request - Invalid request syntax or parameters |
| 401 | Unauthorized - Authentication required or failed |
| 403 | Forbidden - Authenticated but insufficient permissions |
| 404 | Not Found - Resource does not exist |
| 409 | Conflict - Request conflicts with current state |
| 422 | Unprocessable Entity - Validation error in request data |
| 429 | Too Many Requests - Rate limit exceeded |
5xx - Server Error
| Code | Meaning |
|---|---|
| 500 | Internal Server Error - Unexpected server error |
| 502 | Bad Gateway - Invalid response from upstream service |
| 503 | Service Unavailable - Server temporarily unavailable |
| 504 | Gateway Timeout - Request timeout |
Error Code Reference
Authentication Errors (401 Unauthorized)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| INVALID_CREDENTIALS | 401 | Email or password incorrect | Verify credentials and retry login |
| MISSING_AUTHORIZATION | 401 | Authorization header missing | Include Authorization header |
| INVALID_TOKEN | 401 | Malformed or invalid token | Obtain new token via login |
| TOKEN_EXPIRED | 401 | Access token has expired | Use refresh token to get new access token |
| INVALID_REFRESH_TOKEN | 401 | Refresh token invalid or expired | Log in again to obtain new tokens |
Authorization Errors (403 Forbidden)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| FORBIDDEN | 403 | Insufficient permissions for resource | Use account with required role |
| INSUFFICIENT_ROLE | 403 | User role lacks required permission | Request access from administrator |
| ORGANIZATION_ACCESS_DENIED | 403 | Access denied to this organization | Request access from organization administrator |
Validation Errors (422 Unprocessable Entity)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| VALIDATION_ERROR | 422 | One or more fields invalid | See details array for specific fields |
| DE_IDENTIFICATION_ERROR | 422 | Data contains potential identifiers | Remove identifying information |
| DUPLICATE_ENTRY | 422 | Record with same data already exists | Check for existing record |
| INVALID_ENUM_VALUE | 422 | Invalid value for enumerated field | Use one of allowed values |
Not Found Errors (404 Not Found)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| NOT_FOUND | 404 | Requested resource does not exist | Check resource ID |
| PATIENT_NOT_FOUND | 404 | Patient with ID not found | Verify patient ID |
| VISIT_NOT_FOUND | 404 | Visit with ID not found | Verify visit ID |
| BATCH_NOT_FOUND | 404 | Batch processing job not found | Check batch ID |
Rate Limiting (429 Too Many Requests)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| RATE_LIMITED | 429 | Request rate limit exceeded | Wait and retry after reset time |
| BURST_LIMIT_EXCEEDED | 429 | Burst rate limit exceeded | Space out rapid requests |
Server Errors (5xx)
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
| INTERNAL_SERVER_ERROR | 500 | Unexpected server error | Retry request after delay |
| SERVICE_UNAVAILABLE | 503 | Server temporarily unavailable | Retry after some time |
| DATABASE_ERROR | 500 | Database operation failed | Retry request |
| TIMEOUT_ERROR | 504 | Request processing timeout | Reduce request size or retry |
Common Error Scenarios
1. Invalid Credentials (401)
Request:
curl -X POST https://api.menotime.timelessbiotech.com/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"wrongpassword"}'
Response (401 Unauthorized):
{
"status": "error",
"code": "INVALID_CREDENTIALS",
"message": "Invalid email or password",
"details": {
"attempt": 1,
"max_attempts": 5
}
}
Solution: Verify email and password are correct. After 5 failed attempts, account may be temporarily locked.
2. Missing Authorization Header (401)
Request:
curl -X GET https://api.menotime.timelessbiotech.com/v1/patients
Response (401 Unauthorized):
{
"status": "error",
"code": "MISSING_AUTHORIZATION",
"message": "Authorization header is required",
"details": {
"required_header": "Authorization",
"format": "Bearer {token}"
}
}
Solution: Include Authorization header with valid JWT token:
curl -X GET https://api.menotime.timelessbiotech.com/v1/patients \
-H "Authorization: Bearer {access_token}"
3. Expired Access Token (401)
Response (401 Unauthorized):
{
"status": "error",
"code": "TOKEN_EXPIRED",
"message": "Access token has expired. Please refresh your token.",
"details": {
"expired_at": "2024-02-15T15:30:00Z",
"refresh_endpoint": "/auth/refresh"
}
}
Solution: Use refresh token to get new access token:
curl -X POST https://api.menotime.timelessbiotech.com/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"'$REFRESH_TOKEN'"}'
4. Insufficient Permissions (403)
Response (403 Forbidden):
{
"status": "error",
"code": "FORBIDDEN",
"message": "Insufficient permissions for this resource",
"details": {
"required_role": "admin",
"user_role": "readonly",
"resource": "POST /patients"
}
}
Solution: Use an account with the required role (admin, provider, etc.) or request role upgrade from administrator.
5. Validation Error - Invalid Field (422)
Request:
curl -X POST https://api.menotime.timelessbiotech.com/v1/patients \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"age_years":150,"race":"invalid_value"}'
Response (422 Unprocessable Entity):
{
"status": "error",
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "age_years",
"message": "Age must be between 18 and 120"
},
{
"field": "race",
"message": "Invalid value 'invalid_value'. Must be one of: caucasian, african_american, asian, hispanic, native_american, pacific_islander, other, unknown"
}
]
}
Solution: Correct the invalid values according to the error messages.
6. Missing Required Field (422)
Request:
curl -X POST https://api.menotime.timelessbiotech.com/v1/patients \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"age_years":52}'
Response (422 Unprocessable Entity):
{
"status": "error",
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "race",
"message": "This field is required"
},
{
"field": "ethnicity",
"message": "This field is required"
},
{
"field": "region",
"message": "This field is required"
},
{
"field": "menopause_status",
"message": "This field is required"
}
]
}
Solution: Include all required fields in the request.
7. Resource Not Found (404)
Request:
curl -X GET https://api.menotime.timelessbiotech.com/v1/patients/pat_invalid \
-H "Authorization: Bearer {access_token}"
Response (404 Not Found):
{
"status": "error",
"code": "PATIENT_NOT_FOUND",
"message": "Patient not found",
"details": {
"patient_id": "pat_invalid",
"searched_fields": ["id"]
}
}
Solution: Verify the correct patient ID and retry the request.
8. Rate Limit Exceeded (429)
Response (429 Too Many Requests):
{
"status": "error",
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please retry after some time.",
"details": {
"limit": 1000,
"window": "1 hour",
"reset_at": "2024-02-15T15:30:00Z",
"retry_after_seconds": 3600
}
}
Response Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708015800
Retry-After: 3600
Solution: Implement exponential backoff and wait until rate limit window resets. Check Retry-After header for recommended wait time.
9. De-identification Validation Error (422)
Request (contains potential identifier):
curl -X POST https://api.menotime.timelessbiotech.com/v1/patients \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"age_years": 52,
"race": "caucasian",
"ethnicity": "non_hispanic",
"region": "northeast",
"menopause_status": "perimenopause",
"notes": "Patient Jane Doe from Boston MA"
}'
Response (422 Unprocessable Entity):
{
"status": "error",
"code": "DE_IDENTIFICATION_ERROR",
"message": "Data contains potential identifiers",
"details": [
{
"field": "notes",
"warning": "Text contains possible personal name: 'Jane Doe'",
"position": "characters 7-14"
},
{
"field": "notes",
"warning": "Text contains specific location: 'Boston MA'",
"position": "characters 21-30"
}
]
}
Solution: Remove identifying information from the data and retry.
10. Server Error (500)
Response (500 Internal Server Error):
{
"status": "error",
"code": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred",
"details": {
"request_id": "req_12345678",
"timestamp": "2024-02-15T14:30:00Z",
"support": "Contact support with request_id"
}
}
Solution: Note the request_id and contact support. Implement retry logic with exponential backoff.
Error Handling Best Practices
1. Implement Retry Logic
import time
import requests
def retry_request(url, max_retries=3, backoff_factor=2):
for attempt in range(max_retries):
try:
response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if response.status_code in [429, 500, 502, 503, 504]:
wait_time = backoff_factor ** attempt
print(f"Retry after {wait_time} seconds")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")
2. Handle Rate Limiting
import time
def make_request_with_rate_limit(url, headers):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Retrying after {retry_after} seconds")
time.sleep(retry_after)
return make_request_with_rate_limit(url, headers)
return response.json()
3. Validate Before Submission
def validate_patient_data(data):
errors = []
# Check required fields
required = ['age_years', 'race', 'ethnicity', 'region', 'menopause_status']
for field in required:
if field not in data:
errors.append(f"Missing required field: {field}")
# Validate age range
if data.get('age_years'):
if not (18 <= data['age_years'] <= 120):
errors.append("Age must be between 18 and 120")
# Validate enum values
valid_races = ['caucasian', 'african_american', 'asian', 'hispanic', 'native_american', 'pacific_islander', 'other', 'unknown']
if data.get('race') and data['race'] not in valid_races:
errors.append(f"Invalid race. Must be one of: {', '.join(valid_races)}")
return errors
# Usage
data = {"age_years": 52, "race": "invalid"}
errors = validate_patient_data(data)
if errors:
print("Validation errors:", errors)
else:
# Submit to API
pass
4. Log Errors for Debugging
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def make_api_request(url, method='GET', data=None):
try:
if method == 'GET':
response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
else:
response = requests.post(url, json=data, headers={"Authorization": f"Bearer {token}"})
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
error_data = e.response.json()
logger.error(f"API Error: {error_data['code']} - {error_data['message']}")
logger.error(f"Request ID: {e.response.headers.get('X-Request-ID')}")
raise
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
raise
5. Check Response Status
def safe_api_call(url, headers):
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()["data"]
elif response.status_code == 401:
print("Authentication failed. Please re-login.")
elif response.status_code == 403:
print("Access denied. Insufficient permissions.")
elif response.status_code == 404:
print("Resource not found.")
elif response.status_code == 422:
errors = response.json()["details"]
print("Validation errors:", errors)
elif response.status_code == 429:
print("Rate limited. Please wait before retrying.")
elif response.status_code >= 500:
print("Server error. Please try again later.")
else:
print(f"Unexpected status: {response.status_code}")
Error Codes Quick Reference
All Error Codes (Alphabetical)
BURST_LIMIT_EXCEEDED
DATABASE_ERROR
DE_IDENTIFICATION_ERROR
DUPLICATE_ENTRY
FORBIDDEN
INSUFFICIENT_ROLE
INTERNAL_SERVER_ERROR
INVALID_CREDENTIALS
INVALID_ENUM_VALUE
INVALID_REFRESH_TOKEN
INVALID_TOKEN
MISSING_AUTHORIZATION
NOT_FOUND
ORGANIZATION_ACCESS_DENIED
PATIENT_NOT_FOUND
RATE_LIMITED
SERVICE_UNAVAILABLE
TIMEOUT_ERROR
TOKEN_EXPIRED
VALIDATION_ERROR
VISIT_NOT_FOUND
Support
If you encounter persistent errors:
- Check the error code against this reference
- Review error details for specific field names or issues
- Implement retry logic for transient failures
-
Contact support with request_id for unresolved issues
-
Technical Support: api-support@timelessbiotech.com
- HIPAA/Compliance: hipaa-compliance@timelessbiotech.com