Error Handling Guide
Learn how to handle errors gracefully when integrating with the Fuuffy API.
Error Response Format
All API errors follow a consistent format:
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": [
{
"field": "sender.phone",
"message": "Phone number is required"
}
]
}
}
HTTP Status Codes
| Status Code | Description | Common Causes |
|---|---|---|
400 Bad Request | Invalid request | Missing required fields, invalid data format |
401 Unauthorized | Authentication failed | Invalid or expired access token |
403 Forbidden | Insufficient permissions | Accessing resources you don't have access to |
404 Not Found | Resource not found | Invalid order ID or tracking number |
409 Conflict | Resource conflict | Duplicate order, conflicting operation |
422 Unprocessable Entity | Validation failed | Business logic validation errors |
429 Too Many Requests | Rate limit exceeded | Too many requests in a short time |
500 Internal Server Error | Server error | Unexpected server-side error |
503 Service Unavailable | Service unavailable | Maintenance or temporary outage |
Error Codes
Authentication Errors
| Code | Description | Solution |
|---|---|---|
invalid_token | Access token is invalid or expired | Request a new access token |
invalid_client | Client credentials are invalid | Verify your client ID and secret |
unauthorized_client | Client not authorized | Check your account permissions |
Validation Errors
| Code | Description | Solution |
|---|---|---|
validation_error | Request validation failed | Check the details field for specific errors |
missing_field | Required field is missing | Include all required fields |
invalid_format | Field format is invalid | Check field format requirements |
Resource Errors
| Code | Description | Solution |
|---|---|---|
order_not_found | Order not found | Verify the order ID |
tracking_not_found | Tracking info not found | Verify the tracking number |
cannot_cancel | Cannot cancel order | Check order status |
cannot_update | Cannot update order | Check if order is still pending |
Rate Limiting
| Code | Description | Solution |
|---|---|---|
rate_limit_exceeded | Too many requests | Wait and retry, implement exponential backoff |
Error Handling in PHP
Basic Error Handling
<?php
function makeApiRequest($endpoint, $method = 'GET', $data = null, $access_token) {
$ch = curl_init("${API_URL}/v1{$endpoint}");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
// Handle errors
if ($http_code >= 400) {
handleApiError($http_code, $result);
return null;
}
return $result;
}
function handleApiError($http_code, $error_data) {
$error = $error_data['error'] ?? [];
$code = $error['code'] ?? 'unknown_error';
$message = $error['message'] ?? 'An error occurred';
switch ($http_code) {
case 400:
error_log("Bad Request: {$message}");
if (isset($error['details'])) {
foreach ($error['details'] as $detail) {
error_log(" - {$detail['field']}: {$detail['message']}");
}
}
break;
case 401:
error_log("Unauthorized: {$message}");
// Refresh access token
break;
case 404:
error_log("Not Found: {$message}");
break;
case 429:
error_log("Rate Limited: {$message}");
// Implement retry with backoff
break;
case 500:
case 503:
error_log("Server Error: {$message}");
// Retry the request
break;
default:
error_log("API Error ({$http_code}): {$message}");
}
}
?>
Advanced Error Handling with Retry Logic
<?php
class FuuffyApiClient {
private $access_token;
private $max_retries = 3;
public function request($endpoint, $method = 'GET', $data = null) {
$attempt = 0;
while ($attempt < $this->max_retries) {
try {
$result = $this->makeRequest($endpoint, $method, $data);
return $result;
} catch (RateLimitException $e) {
$attempt++;
$wait_time = $this->calculateBackoff($attempt);
error_log("Rate limited. Retrying in {$wait_time}s...");
sleep($wait_time);
} catch (ServerErrorException $e) {
$attempt++;
if ($attempt >= $this->max_retries) {
throw $e;
}
$wait_time = $this->calculateBackoff($attempt);
error_log("Server error. Retrying in {$wait_time}s...");
sleep($wait_time);
} catch (UnauthorizedException $e) {
// Refresh token and retry once
$this->refreshAccessToken();
return $this->makeRequest($endpoint, $method, $data);
}
}
throw new Exception("Max retries exceeded");
}
private function makeRequest($endpoint, $method, $data) {
$ch = curl_init("${API_URL}/v1{$endpoint}");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->access_token,
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
// Throw appropriate exceptions
if ($http_code === 401) {
throw new UnauthorizedException($result['error']['message']);
} elseif ($http_code === 429) {
throw new RateLimitException($result['error']['message']);
} elseif ($http_code >= 500) {
throw new ServerErrorException($result['error']['message']);
} elseif ($http_code >= 400) {
throw new ApiException($result['error']['message'], $http_code);
}
return $result;
}
private function calculateBackoff($attempt) {
// Exponential backoff: 2^attempt seconds
return min(pow(2, $attempt), 60); // Max 60 seconds
}
private function refreshAccessToken() {
// Implement token refresh logic
}
}
// Custom exceptions
class ApiException extends Exception {}
class UnauthorizedException extends ApiException {}
class RateLimitException extends ApiException {}
class ServerErrorException extends ApiException {}
?>
Best Practices
1. Always Check HTTP Status Codes
<?php
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code !== 200 && $http_code !== 201) {
// Handle error
}
?>
2. Implement Exponential Backoff
<?php
function retryWithBackoff($callable, $max_attempts = 3) {
for ($attempt = 1; $attempt <= $max_attempts; $attempt++) {
try {
return $callable();
} catch (Exception $e) {
if ($attempt === $max_attempts) {
throw $e;
}
$wait = pow(2, $attempt);
sleep($wait);
}
}
}
?>
3. Log All Errors
<?php
error_log(sprintf(
"API Error: %s - %s (HTTP %d)",
$error['code'],
$error['message'],
$http_code
));
?>
4. Handle Token Expiration
<?php
if ($http_code === 401) {
$this->refreshAccessToken();
// Retry the request
}
?>
5. Validate Before Sending
<?php
function validateOrderData($data) {
$required = ['sender', 'recipient', 'package'];
foreach ($required as $field) {
if (!isset($data[$field])) {
throw new ValidationException("Missing required field: {$field}");
}
}
}
?>