> For the complete documentation index, see [llms.txt](https://docs.bullcredtech.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bullcredtech.com/api-reference/apis-en/guias/error-handling.md).

# Error handling

This guide documents the HTTP codes returned by the API, conflict scenarios (409), and error messages for robust integration.

## HTTP Codes

| Code  | Meaning               | When                                                                       |
| ----- | --------------------- | -------------------------------------------------------------------------- |
| `200` | OK                    | Successful GET request                                                     |
| `201` | Created               | Resource created successfully (POST)                                       |
| `202` | Accepted              | Asynchronous processing started — use polling or webhook to get the result |
| `400` | Bad Request           | Invalid input data (format, required fields)                               |
| `401` | Unauthorized          | Missing, expired, or invalid token                                         |
| `403` | Forbidden             | No permission to access the resource                                       |
| `404` | Not Found             | Proposal or resource not found                                             |
| `409` | Conflict              | Conflict with the current state of the resource (see section below)        |
| `422` | Unprocessable Entity  | Business validation failed (e.g.: rejection in analysis)                   |
| `500` | Internal Server Error | Internal server error                                                      |
| `503` | Service Unavailable   | Service temporarily unavailable                                            |

## Error format

All errors follow the standard format:

```json
{
  "statusCode": 400,
  "message": "Readable description of the error",
  "error": "Bad Request"
}
```

Some errors include additional fields for programmatic identification:

```json
{
  "statusCode": 409,
  "message": "There is already a signed contract for this proposal.",
  "error": "Conflict",
  "errorCode": "SIGNED_CONTRACT_EXISTS"
}
```

Validation errors may include details about the invalid field:

```json
{
  "statusCode": 400,
  "message": "Invalid bank code",
  "error": "Bad Request",
  "details": {
    "field": "bank_code",
    "validCodes": "See GET /banks for the complete list"
  }
}
```

Status conflict errors include the statuses involved:

```json
{
  "statusCode": 409,
  "message": "Proposal is not in the correct status for this operation",
  "error": "Conflict",
  "details": {
    "currentStatus": "CREATED",
    "requiredStatus": "TOKEN_VALIDATED"
  }
}
```

## 409 Errors - Business Conflicts

### Proposal Creation (`POST /proposals/start`)

| Error Code                        | Message                                                                         | Cause                                                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `CONTRACT_ACTIVE`                 | There is already an active contract with a valid digital signature for this CPF | Digital signature still valid                                                                             |
| `CONTRACT_EXISTS`                 | There is already a contract generated for this CPF                              | Contract already generated                                                                                |
| `PROPOSAL_PENDING_FRAUD_ANALYSIS` | There is already a proposal under fraud analysis for this CPF                   | Fraud analysis in progress                                                                                |
| `PROPOSAL_PROCESSING_IN_PROGRESS` | There is already a proposal being processed for this CPF                        | Active asynchronous processing                                                                            |
| `PROPOSAL_AWAITING_DISBURSEMENT`  | There is already an approved proposal awaiting disbursement for this CPF        | Awaiting disbursement                                                                                     |
| `PROPOSAL_FINALIZED`              | There is already a finalized proposal for this CPF                              | CPF has a contract `SIGNED`/`DISBURSED` and re-contracting is disabled at the origin                      |
| `DELINQUENT_CONTRACT`             | It is currently not possible to proceed with the contract                       | CPF has a contract with overdue installments (`daysLate > 0`) — re-contracting blocked due to delinquency |

{% hint style="info" %}
The errors `PROPOSAL_FINALIZED` and `DELINQUENT_CONTRACT` are specific to the **re-contracting**. See the guide [Re-contracting](/api-reference/apis-en/guias/recontracting.md) to understand the full logic.
{% endhint %}

### Contact (`POST /proposals/{id}/contact`)

| Message                                                     | Cause                                            |
| ----------------------------------------------------------- | ------------------------------------------------ |
| This phone number is already being used in another proposal | Phone number already associated with another CPF |

### Credit Analysis (`POST /proposals/{id}/credit-analysis`)

| Error Code                          | Message                                    | Cause                          |
| ----------------------------------- | ------------------------------------------ | ------------------------------ |
| `CREDIT_ANALYSIS_ALREADY_COMPLETED` | Credit analysis has already been performed | Result already available       |
| -                                   | Credit analysis is already in progress     | Active asynchronous processing |

**Recommended action:** Wait for processing or check `GET /proposals/{id}` for the result.

### Pre-acceptance (`POST /proposals/{id}/pre-accept`)

| Error Code                     | Message                                                            | Cause                   |
| ------------------------------ | ------------------------------------------------------------------ | ----------------------- |
| `INVALID_STATUS_FOR_PREACCEPT` | It is not possible to pre-accept a proposal with an invalid status | Invalid status          |
| `SIGNED_CONTRACT_EXISTS`       | There is already a signed contract for this proposal               | Contract already signed |

### Contract Generation (`POST /proposals/{id}/contract`)

| Error Code                        | Message                                                            | Cause                    |
| --------------------------------- | ------------------------------------------------------------------ | ------------------------ |
| `SIGNED_CONTRACT_EXISTS`          | There is already a signed contract for this proposal               | Contract already signed  |
| `CONTRACT_GENERATION_IN_PROGRESS` | Contract generation is already in progress                         | Active processing        |
| `CONTRACT_ALREADY_GENERATED`      | Contract has already been generated                                | Result already available |
| `PROPOSAL_FINALIZED`              | Proposal already finalized                                         | Proposal in final state  |
| `CONTRACT_ACTIVE`                 | There is already an active contract with a valid digital signature | Existing valid signature |

## Credit Analysis - Responses

Credit analysis can return different results with friendly messages:

| Scenario                 | HTTP | Message                                           |
| ------------------------ | ---- | ------------------------------------------------- |
| Approved with offers     | 200  | Analysis completed with available offers          |
| Rejected - no margin     | 200  | Unfortunately, we could not find available margin |
| Rejected - irregular CPF | 200  | It was not possible to proceed with the analysis  |
| Rejected - age           | 200  | The customer does not meet the age criteria       |
| Credit engine error      | 500  | Internal error in credit analysis                 |
| Asynchronous processing  | 202  | Analysis in progress, wait for the result         |

{% hint style="info" %}
**Note**

Even when the analysis results in rejection, the HTTP code is `200`. The field `approved` in the response body indicates whether the proposal was approved or not.
{% endhint %}

## Asynchronous Processing (409 during processing)

The **credit analysis** and **contract generation** may return `409` during processing:

```json
{
  "statusCode": 409,
  "message": "Credit analysis is already in progress for this proposal. Wait 30 seconds or check the proposal status."
}
```

### How to handle:

1. **Wait** the suggested time in the message
2. **Check** `GET /proposals/{id}` to verify the current status
3. **Call the endpoint again** — if processing is complete, it returns the result
4. **Use webhooks** to receive notification when processing finishes

## Handling Strategies

### For 400 errors (Validation)

* Check the format of the sent data
* Confirm that all required fields are present
* Validate CPF, phone number, and email before sending
* Do not try again without correcting the data

### For 401 errors (Authentication)

* Obtain a new access token
* Check that the token has not expired (validity: 1 hour)
* Confirm that the token is valid and has not expired

### For 409 errors (Conflict)

* Read the `errorCode` and the `message` to understand the scenario
* Check `GET /proposals/{id}` to verify the current state
* Wait for asynchronous processes before trying again

### For 500 and 503 errors (Server)

Implement retry with **exponential backoff**:

| Attempt | Wait before retry       |
| ------- | ----------------------- |
| 1st     | 1 second                |
| 2nd     | 2 seconds               |
| 3rd     | 4 seconds               |
| 4th+    | Report to the Bull team |

```javascript
async function requestWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (err.statusCode < 500 || attempt === maxRetries - 1) throw err;
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
    }
  }
}
```

### For 422 errors (Credit Analysis)

HTTP 422 indicates rejection due to business rule. Examples:

```json
{
  "statusCode": 422,
  "message": "Proposal rejected in credit analysis",
  "error": "Unprocessable Entity",
  "details": { "reason": "Insufficient credit score" }
}
```

```json
{
  "statusCode": 422,
  "message": "Insufficient payroll-deductible margin",
  "error": "Unprocessable Entity",
  "details": {
    "availableMargin": "500.00",
    "minimumRequired": "1000.00"
  }
}
```

Do not try again — the decision is made by the credit policy. Consult `GET /proposals/{id}/analysis-data` for more details about the reason for the rejection.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.bullcredtech.com/api-reference/apis-en/guias/error-handling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
