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

StatusCodeDescription
200OKEverything worked as expected.
201CreatedSuccessfully created a new resource.
401UnauthorizedNo valid API key provided.
403ForbiddenThe API key doesn’t have permissions to perform the request.
404Not FoundThe requested resource doesn’t exist or you do not have permission to access it.
409ConflictThe request conflicts with another request (perhaps due to using the same idempotent key).
422Unprocessable EntityThe request was unacceptable, often due to missing a required parameter.
429Too Many RequestsToo many requests hit the API too quickly. We recommend an exponential backoff of your requests.
5xxServer ErrorSomething went wrong on Perch’s end. (These are rare.)

Error response format

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:
CodeDescription
INVALIDThe field does not follow the intended format, e.g., an invalid email address.
INCORRECT_TYPEThe field has a value of an incorrect type, e.g., a Boolean passed to a Date field.
NOT_UNIQUEThere’s a conflict in the database on this field, e.g., a unique constraint is violated.
NOT_FOUNDNo records could be found for a lookup using this field.
UNSUPPORTEDThe field type is unsupported. Check the supported field types in the Changeset.
REQUIREDThe field is required but is undefined or missing.
IN_USEThe 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

  1. Always check status codes - Don’t assume requests succeeded without checking the HTTP status
  2. Parse error messages - The message field often contains human-readable explanations
  3. Handle field errors - Use the fields object to show specific validation errors to users
  4. Implement retry logic - For 429 and 5xx errors, implement smart retry mechanisms
  5. Log errors appropriately - Log enough detail for debugging but avoid logging sensitive data
  6. 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
}