> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myperch.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and response codes

> Understanding error handling and HTTP status codes in the Perch Core API

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.)                                           |

## 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.

```json theme={null}
// 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

<Warning>
  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.
</Warning>

## 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

```javascript theme={null}
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
}
```
