Error Handling
Understanding and handling API errors.
HTTP Status Codes
| Code | Meaning | Action |
|---|---|---|
200 | Success | Request completed |
201 | Created | Resource created |
400 | Bad Request | Check request format |
401 | Unauthorized | Token expired or invalid |
403 | Forbidden | Insufficient permissions |
404 | Not Found | Resource doesn't exist |
422 | Validation Error | Fix request data |
429 | Rate Limited | Slow down requests |
500 | Server Error | Retry or contact support |
Error Response Format
All errors return a consistent JSON structure:
{
"statusCode": 422,
"error": "Unprocessable Entity",
"message": "Validation failed",
"details": [
{
"field": "consumer.email",
"message": "\"email\" must be a valid email"
}
]
}
Handling Errors in Code
- TypeScript
interface ApiError {
statusCode: number;
error: string;
message: string;
details?: Array<{
field: string;
message: string;
}>;
}
async function createOrder(order: OrderPayload): Promise<Order> {
const response = await fetch("https://na1-prod.okcapsule.app/v2/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(order),
});
if (!response.ok) {
const error: ApiError = await response.json();
switch (response.status) {
case 401:
// Token expired - refresh and retry
await refreshToken();
return createOrder(order);
case 422:
// Validation error - log details
console.error("Validation errors:", error.details);
throw new Error(`Validation failed: ${error.message}`);
case 429:
// Rate limited - wait and retry
const retryAfter = response.headers.get("Retry-After") || "60";
await sleep(parseInt(retryAfter) * 1000);
return createOrder(order);
default:
throw new Error(`API error: ${error.message}`);
}
}
const data = await response.json();
return data.order;
}
Common Validation Errors
Consumer Errors
| Error | Cause | Fix |
|---|---|---|
"email" must be a valid email | Invalid email format | Check email syntax |
"last_name" is required | Missing required field | Include last_name with first_name |
"phone_number" length must be less than or equal to 15 | Phone too long | Use E.164 format |
Address Errors
| Error | Cause | Fix |
|---|---|---|
"country_name" is required | Missing country | Add country_name |
"address1" length must be less than or equal to 100 | Address too long | Shorten address |
Invalid postal code for country | Postal code mismatch | Verify postal code format |
Order Errors
| Error | Cause | Fix |
|---|---|---|
"order_lines" must contain at least 1 items | Empty order | Add order_lines with pouches |
Invalid client_product_id | Product not found | Check product exists and is active |
Either pack_id or contents required | Empty pouch | Add contents or pack_id |
Maximum 8 supplements per pouch | Too many items | Reduce pouch contents |
Product Errors
| Error | Cause | Fix |
|---|---|---|
Product is not active | Using inactive product | Filter for active=true |
Product not found | Invalid product ID | Verify product ID |
Authentication Errors
401 Unauthorized
{
"statusCode": 401,
"error": "Unauthorized",
"message": "Token expired"
}
Solution: Refresh your access token:
async function refreshToken(): Promise<string> {
const response = await fetch(
"https://na1-prod.okcapsule.app/v2/authentication/refresh",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token }),
}
);
const data = await response.json();
return data.access_token;
}
403 Forbidden
{
"statusCode": 403,
"error": "Forbidden",
"message": "Insufficient permissions"
}
Solution: Your user lacks the required permission scope. Contact your admin to update role permissions.
Rate Limiting
When rate limited, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Best practices:
- Implement exponential backoff
- Cache responses when possible
- Batch operations where supported
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
await sleep(delay);
continue;
}
throw error;
}
}
throw new Error("Max retries exceeded");
}
Debugging Tips
- Check the full error response - Details array contains field-specific errors
- Validate before sending - Use the field constraints from Create Orders
- Test in Stage first - Use the stage environment for development
- Log request/response - Include correlation IDs for support tickets
Getting Help
If you encounter persistent errors:
- Check the Interactive API Docs for endpoint details
- Verify your request matches the expected schema
- Contact OK Capsule support with:
- Request URL and method
- Request body (redact sensitive data)
- Full error response
- Timestamp of the request