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

# Errors

> Complete error reference for the Recotap External API

All errors use a consistent envelope. The exact shape differs slightly between validation errors and business logic errors.

**Validation errors** (`400` from DTO checks, fired before any database call):

```json theme={null}
{
  "statusCode": 400,
  "timestamp": "2026-04-28T10:15:00.000Z",
  "path": "/api/v1/accounts",
  "message": "Bad Request",
  "data": [
    {
      "property": "domain",
      "constraints": { "isNotEmpty": "domain should not be empty" }
    }
  ]
}
```

`data` contains the raw class-validator error objects. There is no `customMessage` on validation errors.

**Business logic errors** (thrown by the service or controller):

```json theme={null}
{
  "statusCode": 400,
  "timestamp": "2026-04-28T10:15:00.000Z",
  "path": "/api/v1/accounts",
  "message": "Bad Request",
  "customMessage": "Segment 'abc' is not active or draft and cannot accept new members",
  "data": null
}
```

`customMessage` is present only when the service explicitly sets it. Use it for logging and user-facing messages.

## 401 Unauthorized

Applies to every endpoint.

| Condition                  | `customMessage`                |
| -------------------------- | ------------------------------ |
| `X-Api-Key` header missing | `X-Api-Key header is required` |
| Key not found or invalid   | `Invalid API key`              |

## 400 Bad Request — validation

Fires before any database call. `data` contains the raw class-validator error objects.

| Endpoint                       | Field / rule                              | Example `customMessage`                                              |
| ------------------------------ | ----------------------------------------- | -------------------------------------------------------------------- |
| `POST /accounts`               | `accounts` — empty or missing             | `accounts must be an array`                                          |
| `POST /accounts`               | `accounts` — more than 100 items          | `accounts must contain no more than 100 elements`                    |
| `POST /accounts`               | `domain` — missing                        | `domain should not be empty`                                         |
| `POST /accounts`               | `name` — missing                          | `name should not be empty`                                           |
| `POST /deals`                  | `deals` — more than 100 items             | `deals must contain no more than 100 elements`                       |
| `POST /deals`                  | `externalDealId` — missing                | `externalDealId should not be empty`                                 |
| `POST /deals`                  | `startDate` / `closedDate` — not ISO 8601 | `startDate must be a valid ISO 8601 date string`                     |
| `POST /deals`                  | `ownerEmail` — not a valid email          | `ownerEmail must be an email`                                        |
| `POST /sales-activities`       | `activities` — more than 50 items         | `activities must contain no more than 50 elements`                   |
| `POST /sales-activities`       | `occurredAt` — missing or not ISO 8601    | `occurredAt must be a valid ISO 8601 date string`                    |
| `POST /sales-activities`       | `domain` — missing                        | `domain should not be empty`                                         |
| `POST /sales-activities`       | `ownerEmail` — missing or invalid         | `ownerEmail must be an email`                                        |
| `POST /sales-activities`       | `contacts` — missing or empty             | `contacts must contain at least 1 elements`                          |
| `POST /sales-activities`       | `direction` — not `inbound` or `outbound` | `direction must be one of the following values: inbound, outbound`   |
| `POST /accounts/custom-field`  | `label` — missing                         | `label should not be empty`                                          |
| `POST /accounts/custom-field`  | `labelType` — invalid value               | `labelType must be one of the following values: singleLineText, ...` |
| `POST /accounts/custom-field`  | `options` — missing when required         | `options must contain at least 1 elements`                           |
| `GET /accounts`                | `lastSync` — invalid format               | `lastSync must be a valid ISO 8601 date string`                      |
| `PATCH /accounts/external-ids` | `mappings` — more than 100 items          | `mappings must contain no more than 100 elements`                    |

## 400 Bad Request — business logic

| Endpoint                 | Condition                                     | `customMessage`                                                        |
| ------------------------ | --------------------------------------------- | ---------------------------------------------------------------------- |
| `POST /accounts`         | `segmentId` not found                         | `Segment '<id>' not found`                                             |
| `POST /accounts`         | `segmentId` is a dynamic segment              | `Segment '<id>' is not a static segment`                               |
| `POST /accounts`         | `segmentId` status is not `active` or `draft` | `Segment '<id>' is not active and cannot accept new members`           |
| `PUT /accounts/:rtp_aid` | `rtp_aid` is not a valid format               | `Invalid rtp_aid format`                                               |
| `PUT /accounts/:rtp_aid` | `customFields` keys not defined               | `Custom field key(s) not found: KEY_C. Unable to update this account.` |

## 404 Not Found

| Endpoint                 | Condition                    | `customMessage`                         |
| ------------------------ | ---------------------------- | --------------------------------------- |
| `PUT /accounts/:rtp_aid` | No account matches `rtp_aid` | `Account with rtp_aid '<id>' not found` |

## 500 Internal Server Error

`message` is always `Internal server error`. Do not parse `customMessage` — use it for logging only. Safe to retry with exponential back-off.

## Per-item errors in batch endpoints

`POST /accounts`, `POST /deals`, and `POST /sales-activities` always return HTTP 200. Check each item's `status` field in `results`.

| Endpoint                 | Item `status` | Cause                                                                  |
| ------------------------ | ------------- | ---------------------------------------------------------------------- |
| `POST /accounts`         | `failed`      | Domain already exists — use `PUT /accounts/:rtp_aid` to update         |
| `POST /accounts`         | `failed`      | Unknown custom field key in `customFields`                             |
| `POST /deals`            | `failed`      | Pipeline already exists e.g. `Pipeline(s) already exist: smb-pipeline` |
| `POST /sales-activities` | `failed`      | Duplicate `externalActivityId`                                         |
| `POST /sales-activities` | `skipped`     | `activityType` is not `call` or `email`                                |
