Perch uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx
range indicate success. Codes in the 4xx
range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a charge failed, etc.). Codes in the 5xx
range indicate an error with Perch’s servers (these are rare, but they can happen).
HTTP status codes
Status | Code | Description |
---|
200 | OK | Everything worked as expected. |
201 | Created | Successfully created a new resource. |
401 | Unauthorized | No valid API key provided. |
403 | Forbidden | The API key doesn’t have permissions to perform the request. |
404 | Not Found | The requested resource doesn’t exist or you do not have permission to access it. |
409 | Conflict | The request conflicts with another request (perhaps due to using the same idempotent key). |
422 | Unprocessable Entity | The request was unacceptable, often due to missing a required parameter. |
429 | Too Many Requests | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. |
5xx | Server Error | Something went wrong on Perch’s end. (These are rare.) |
The error will contain a helpful message
and, in some cases (409
and 422
), a fields
object. The fields object will contain fields that failed some validation and an array of validation errors.
// 409 - Conflict
// This error indicates that a duplicate record exists for the parent
// (resourceId and resourceType polymorphic association) and the supplied email address.
{
"message": "Failed to create the Lead",
"fields": {
"resourceId": [{
"code": "NOT_UNIQUE",
"message": "Failed to pass unique index constraint",
}],
"resourceType": [{
"code": "NOT_UNIQUE",
"message": "Failed to pass unique index constraint",
}],
"email": [{
"code": "NOT_UNIQUE",
"message": "Failed to pass unique index constraint",
}]
}
}
Field validation error codes
When validation fails on specific fields, you’ll receive one of these error codes:
Code | Description |
---|
INVALID | The field does not follow the intended format, e.g., an invalid email address. |
INCORRECT_TYPE | The field has a value of an incorrect type, e.g., a Boolean passed to a Date field. |
NOT_UNIQUE | There’s a conflict in the database on this field, e.g., a unique constraint is violated. |
NOT_FOUND | No records could be found for a lookup using this field. |
UNSUPPORTED | The field type is unsupported. Check the supported field types in the Changeset. |
REQUIRED | The field is required but is undefined or missing. |
IN_USE | The field is in use by another record. |
Common error scenarios
Authentication errors
401 Unauthorized
- Missing
X-API-Key
header
- Invalid or expired API key
- API key format is incorrect
403 Forbidden
- API key doesn’t have permission for the requested resource
- Trying to access a resource outside your firm’s scope
Client errors
404 Not Found
- Resource doesn’t exist (lead, plan, etc.)
- Typo in the endpoint URL
- Resource exists but you don’t have permission to access it
409 Conflict
- Duplicate lead with same email for your firm
- Idempotency key collision
- Resource state conflict
422 Unprocessable Entity
- Missing required fields
- Invalid field values or formats
- Business logic validation failures
Rate limiting
429 Too Many Requests
- Exceeded your hourly rate limit
- Too many requests in a short burst
- Implement exponential backoff and retry logic
For 429
errors, implement exponential backoff in your retry logic. Start with a 1-second delay and double it with each retry, up to a maximum of 60 seconds.
Best practices for error handling
- Always check status codes - Don’t assume requests succeeded without checking the HTTP status
- Parse error messages - The
message
field often contains human-readable explanations
- Handle field errors - Use the
fields
object to show specific validation errors to users
- Implement retry logic - For
429
and 5xx
errors, implement smart retry mechanisms
- Log errors appropriately - Log enough detail for debugging but avoid logging sensitive data
- Graceful degradation - Handle API errors gracefully in your user interface
Example error handling
try {
const response = await fetch('/api/firmMembers/abc123/leads', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify(leadData)
});
if (!response.ok) {
const errorData = await response.json();
switch (response.status) {
case 401:
throw new Error('Authentication failed - check your API key');
case 403:
throw new Error('Permission denied - contact support');
case 409:
throw new Error(`Conflict: ${errorData.message}`);
case 422:
// Handle field validation errors
handleValidationErrors(errorData.fields);
break;
case 429:
// Implement retry with backoff
await retryWithBackoff(() => makeRequest());
break;
default:
throw new Error(`API error: ${errorData.message}`);
}
}
const data = await response.json();
return data;
} catch (error) {
console.error('API request failed:', error);
// Handle error appropriately in your application
}