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

# Errors

All Partner API errors return a consistent JSON envelope with a machine-readable `code` field. Build your error handling logic around the `code`, not the `message`.

***

## Error Envelope

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description",
    "details": { ... },
    "requestId": "a1b2c3d4-..."
  }
}
```

***

## Complete Error Reference

### Authentication & Authorization Errors

| HTTP Status | Code                         | Cause                                                                                                  | How to Fix                                                                                              |
| ----------- | ---------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `401`       | `UNAUTHORIZED`               | API key is missing, invalid, expired, or the associated partner account is suspended                   | Verify your `x-api-key` header is set correctly; contact support if the key is valid but still rejected |
| `403`       | `FORBIDDEN`                  | Valid API key but missing the required permission for this endpoint                                    | Request the missing permission from the CleanLife team                                                  |
| `403`       | `INVALID_PARTNER_DOMAIN`     | The `Origin` or `Referer` header on your request does not match any registered domain for this partner | Register your domain with CleanLife or remove the `Origin` header for server-to-server calls            |
| `403`       | `SANDBOX_DOMAIN_NOT_ALLOWED` | Your request came from a domain registered as a sandbox domain; sandbox access is currently disabled   | Contact CleanLife to register a production domain                                                       |

***

### Validation Errors

| HTTP Status | Code               | Cause                                                                                                                     | How to Fix                                                              |
| ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `400`       | `VALIDATION_ERROR` | One or more request fields failed validation (wrong type, missing required field, out-of-range value, invalid UUID, etc.) | Read the `details` field for a breakdown of which fields failed and why |

**Example — Missing required field:**

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "serviceId must be a UUID",
    "details": {
      "message": ["serviceId must be a UUID"],
      "statusCode": 400,
      "error": "Bad Request"
    },
    "requestId": "..."
  }
}
```

**Common validation failures:**

| Field                | Validation Rule                                               | Error                                       |
| -------------------- | ------------------------------------------------------------- | ------------------------------------------- |
| `serviceId`          | Must be a valid UUID v4; required                             | `serviceId must be a UUID`                  |
| `addressId`          | Required on `GET /partners/services`; must be a valid UUID v4 | `addressId must be a UUID`                  |
| `contactId`          | Must be a valid UUID v4; required on bookings                 | `contactId must be a UUID`                  |
| `phone`              | Must be a valid Saudi phone number (contacts endpoint)        | `phone must be a valid phone number`        |
| `date`               | Must be an ISO 8601 date string                               | `date must be a valid ISO 8601 date string` |
| `timeslot.startAt`   | Must be a string                                              | `timeslot.startAt must be a string`         |
| `limit` (pagination) | Must be integer 1–100                                         | `limit must not be greater than 100`        |

***

### Resource Errors

| HTTP Status | Code                  | Cause                                                                                                     | How to Fix                                                              |
| ----------- | --------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `404`       | `RESOURCE_NOT_FOUND`  | Booking, contact, address, or service not found — either does not exist or belongs to a different partner | Verify the ID and that the resource was created by your partner account |
| `404`       | `SERVICE_NOT_ALLOWED` | The `serviceId` is not enabled for your partner account or not available at the address                   | Use a `serviceId` from `GET /partners/services?addressId=...`           |

***

### Booking Business Rule Errors

| HTTP Status | Code                              | Cause                                                                                                      | How to Fix                                                                                 |
| ----------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `409`       | `DUPLICATE_EXTERNAL_REFERENCE`    | You attempted to create a booking with an `externalReference` that already exists for your partner account | Use a unique `externalReference` value per booking                                         |
| `422`       | `BOOKING_CREATION_FAILED`         | The booking could not be completed — typically a payment or validation failure                             | Review your booking request fields; retry once; if persistent, contact support             |
| `422`       | `BOOKING_ALREADY_CANCELLED`       | Attempted to cancel a booking that is already cancelled                                                    | No action needed; the booking is already cancelled                                         |
| `422`       | `BOOKING_NOT_PAYABLE`             | Attempted to confirm payment for a cancelled or failed booking                                             | Payment can only be confirmed for active bookings                                          |
| `422`       | `PAYMENT_RESPONSIBILITY_MISMATCH` | Attempted to call `confirm-payment` but your partner account has `paymentResponsibility = CLEANOS`         | Only `PARTNER` responsibility accounts can confirm payments                                |
| `422`       | `PAYMENT_ALREADY_CONFIRMED`       | Attempted to confirm payment for a booking whose payment was already confirmed                             | Payment confirmation is idempotent in intent but rejected as a duplicate; no action needed |

***

### Rate Limit Errors

| HTTP Status | Code                  | Cause                                                                             | How to Fix                                                            |
| ----------- | --------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `429`       | `RATE_LIMIT_EXCEEDED` | Exceeded your API key's request limit within the current 60-second sliding window | Wait for the window to reset and retry; implement exponential backoff |

***

### Server Errors

| HTTP Status | Code             | Cause                        | How to Fix                                                                                            |
| ----------- | ---------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| `500`       | `INTERNAL_ERROR` | Unexpected server-side error | Retry with exponential backoff; if persistent, contact support with the `requestId` from the response |

***

## Using `requestId` for Support

Every response (including errors) includes a `requestId` in the error envelope and an `X-Request-Id` response header. If you contact CleanLife support about an unexpected error, always include this value — it allows the engineering team to trace the exact request through server logs.

```bash theme={null}
# See the request ID in the response header
curl -i -X GET "https://apiv3.thecleanlife.dev/v1/partners/pricing/tiers" \
  -H "x-api-key: YOUR_KEY"
# → X-Request-Id: a1b2c3d4-0000-0000-0000-000000000001
```

***

## Retry Strategy

| Error Code                     | Retryable? | Notes                                                                 |
| ------------------------------ | ---------- | --------------------------------------------------------------------- |
| `VALIDATION_ERROR`             | No         | Fix the request payload before retrying                               |
| `UNAUTHORIZED`                 | No         | Fix the API key before retrying                                       |
| `FORBIDDEN`                    | No         | Request the missing permission                                        |
| `RESOURCE_NOT_FOUND`           | No         | The resource does not exist                                           |
| `DUPLICATE_EXTERNAL_REFERENCE` | No         | Use a different `externalReference`                                   |
| `BOOKING_CREATION_FAILED`      | Yes        | Retry once with the same or corrected payload                         |
| `RATE_LIMIT_EXCEEDED`          | Yes        | Back off and retry after the 60-second window                         |
| `INTERNAL_ERROR`               | Yes        | Retry with exponential backoff (see [Best Practices](best-practices)) |
