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

# Errors

> The JSON error envelope returned by every Salesfinity API endpoint, what each status code means, and how to recover from it.

Every Salesfinity API error is JSON. There is no HTML error page, no plain-text fallback, and no
endpoint that answers a failure differently — a client can parse a failure the same way on every
route and every status code.

## The error envelope

```json theme={null}
{
  "message": "Forbidden resource",
  "error": "Forbidden",
  "statusCode": 403
}
```

| Field        | Type                   | Description                                                                                                                                                   |
| ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`    | string or string array | Human-readable description of what went wrong. Becomes an **array of strings** when request validation fails, with one entry per field that did not validate. |
| `error`      | string                 | The HTTP reason phrase for the status, for example `Forbidden`, `Not Found`, `Bad Request`. Stable across releases — branch on this.                          |
| `statusCode` | integer                | The HTTP status code, repeated in the body so it survives proxies and transports that drop it.                                                                |

<Note>
  Branch on `error` or `statusCode`, never on `message`. Message text is written for humans and may
  be reworded; `error` and `statusCode` are contractual.
</Note>

## Status codes

### 400 Bad Request

The request body or query parameters failed validation. `message` is an array with one entry per
invalid field.

```json theme={null}
{
  "message": [
    "limit must not be greater than 100",
    "type must be one of the following values: work, personal"
  ],
  "error": "Bad Request",
  "statusCode": 400
}
```

**Recover by** reading each entry in `message` and correcting the named field. Check the request
against the schema shown on the operation's reference page. Retrying an identical request will
fail identically.

### 402 Payment Required

The team has run out of enrichment credits. Only the enrichment endpoints return this.

```json theme={null}
{
  "message": "Insufficient enrichment credits",
  "error": "Payment Required",
  "statusCode": 402
}
```

**Recover by** topping up credits in the dashboard, then retrying. Call
[Get Enrichment Credits](/api-reference/endpoint/get-enrichment-credits) before a bulk run to
avoid hitting this mid-batch.

### 403 Forbidden

The `x-api-key` header is missing, malformed, revoked, or belongs to a different team.

```json theme={null}
{
  "message": "Forbidden resource",
  "error": "Forbidden",
  "statusCode": 403
}
```

<Warning>
  Salesfinity returns **403 for authentication failures, not 401**. A client written around the
  usual "re-authenticate on 401" convention will never trigger its auth-recovery path against this
  API. Handle 403 explicitly.
</Warning>

**Recover by** checking that the `x-api-key` header is present and spelled correctly, that the key
has not been revoked in **Settings → Connections & API**, and that it belongs to the team whose
data you are requesting.

### 404 Not Found

Either the route does not exist, or the resource does exist but belongs to another team.

```json theme={null}
{
  "message": "Cannot GET /v1/unknown-route",
  "error": "Not Found",
  "statusCode": 404
}
```

A `message` of the form `Cannot <METHOD> <path>` means the route itself is wrong — check the
method and path against the reference. Any other message means the route was correct but the
record was not found for this team.

**Recover by** verifying the path, the HTTP method, and any ID in the URL. Because API keys are
team-scoped, the API returns 404 rather than 403 for records owned by another team; it does not
confirm that they exist.

### 429 Too Many Requests

The client is being throttled.

```json theme={null}
{
  "message": "Too many requests",
  "error": "Too Many Requests",
  "statusCode": 429
}
```

**Recover by** backing off and retrying with exponential backoff and jitter. Salesfinity does not
currently publish a fixed request quota, so treat 429 as a signal to reduce concurrency rather
than as a fixed budget to compute against.

### 500 Internal Server Error

Something failed on the Salesfinity side.

```json theme={null}
{
  "message": "Internal server error",
  "error": "Internal Server Error",
  "statusCode": 500
}
```

**Recover by** retrying with exponential backoff. If it persists, email
[hello@salesfinity.co](mailto:hello@salesfinity.co) with the request path and the timestamp.

## Which errors are retryable

| Status | Retryable | Notes                                                          |
| ------ | --------- | -------------------------------------------------------------- |
| 400    | No        | The request is malformed; an identical retry fails identically |
| 402    | No        | Requires topping up credits first                              |
| 403    | No        | Requires a valid key                                           |
| 404    | No        | Requires a correct path or ID                                  |
| 429    | **Yes**   | Back off with exponential jitter                               |
| 500    | **Yes**   | Back off with exponential jitter                               |

`POST` endpoints are not idempotent. Cap retries on writes, and confirm state with a `GET` before
retrying a create so you do not duplicate a record.

## Handling errors in code

<CodeGroup>
  ```js Node.js theme={null}
  async function salesfinity(path, init = {}) {
    const res = await fetch(`https://client-api.salesfinity.co${path}`, {
      ...init,
      headers: { "x-api-key": process.env.SALESFINITY_API_KEY, ...init.headers },
    });

    if (res.ok) return res.json();

    // Every failure is JSON with the same three fields.
    const { message, error, statusCode } = await res.json();
    const detail = Array.isArray(message) ? message.join("; ") : message;

    const err = new Error(`Salesfinity ${statusCode} ${error}: ${detail}`);
    err.statusCode = statusCode;
    err.retryable = statusCode === 429 || statusCode >= 500;
    throw err;
  }
  ```

  ```python Python theme={null}
  import os, requests

  class SalesfinityError(Exception):
      def __init__(self, body):
          self.status_code = body["statusCode"]
          self.error = body["error"]
          message = body["message"]
          self.detail = "; ".join(message) if isinstance(message, list) else message
          self.retryable = self.status_code == 429 or self.status_code >= 500
          super().__init__(f"Salesfinity {self.status_code} {self.error}: {self.detail}")


  def salesfinity(path, **kwargs):
      res = requests.get(
          f"https://client-api.salesfinity.co{path}",
          headers={"x-api-key": os.environ["SALESFINITY_API_KEY"]},
          timeout=30,
          **kwargs,
      )
      if not res.ok:
          raise SalesfinityError(res.json())
      return res.json()
  ```
</CodeGroup>

## Reporting a problem

Email [hello@salesfinity.co](mailto:hello@salesfinity.co) or use the
[help center](https://support.salesfinity.ai). Include:

* The request method and path, for example `GET /v1/call-log`.
* The timestamp of the request, with its timezone.
* The `error` and `statusCode` from the response body.

Never include your API key in a support message.
