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

# Business Rules

> Hidden business logic: payment flows, booking states, cancellation rules.

This document explains the non-obvious business logic hidden within the Partner API. Understanding these rules is critical to building a robust integration.

***

## Booking Creation

### Payment Responsibility Determines the Entire Flow

Your partner account's `paymentResponsibility` setting governs what happens after you create a booking:

| Responsibility | Booking Created    | Payment                                                       | Booking Status                             |
| -------------- | ------------------ | ------------------------------------------------------------- | ------------------------------------------ |
| `CLEANOS`      | Booking is created | CleanLife sends a payment link to the customer asynchronously | `waiting payment` or `in progress` if free |
| `PARTNER`      | Booking is created | Partner collects payment externally                           | Always `in progress` immediately           |

**For `PARTNER` responsibility partners:** Your booking will always start as `in progress`. CleanLife does not send any payment request. You must call `POST /partners/bookings/:bookingId/confirm-payment` after collecting payment.

**For `CLEANOS` responsibility partners:** The booking may start as `waiting payment`. Poll `GET /partners/bookings/:bookingId/status` to track payment outcome.

***

### Contact and Address IDs

Obtain `contactId` and `addressId` from `POST /partners/contacts` before creating a booking. Use the same `addressId` with `GET /partners/services` to list services available at that location.

***

### Service Catalog Is Address-Scoped

`GET /partners/services` requires an `addressId` and returns **categories**, each with a nested `services` array. Only services that:

* Are enabled for your partner account (when restrictions are configured), **and**
* Are offered in the service zone covering the address coordinates

…are included. Pick `serviceId` from `services[].id` inside the category you display to the customer.

An empty `data` array means the address is outside service coverage or no enabled service is available there — not an error.

***

### Partner-Specific Pricing

Each service returned by `GET /partners/services` includes a `price` field. For your partner account, CleanLife may configure a **custom price per service**.

| Scenario                                              | `price` in catalog                | Booking behavior                                    |
| ----------------------------------------------------- | --------------------------------- | --------------------------------------------------- |
| Deferred payment (`0` or null in allowed-services)    | `0`                               | Booked immediately → `success` (no upfront payment) |
| Partner price configured (> 0)                        | Your negotiated rate (e.g. `150`) | Same partner rate — locked at booking creation      |
| No partner allowed-service row (unrestricted partner) | Platform default price            | Platform default price                              |

**Key rules:**

* You do **not** send a price in the booking request. The platform applies the configured partner price automatically based on the `serviceId` you choose.
* When `price` is `0` in the catalog, the service uses **deferred payment** — the booking is created and confirmed immediately (`success`) with no upfront payment required.
* Always display the `price` from `GET /partners/services` to your customers — it is the authoritative amount for your integration.
* The price is locked when the booking is created and does not change afterward.

***

### External Reference Is Your Safety Net

The `externalReference` field is the most important tool for preventing duplicate bookings:

* It is unique per partner account.
* If you attempt to create a booking with a duplicate `externalReference`, the API returns `409 DUPLICATE_EXTERNAL_REFERENCE`.
* You should generate a unique `externalReference` from your own order system before calling the API.
* In the event of a timeout or network error on your side, you can retry the creation request with the **same** `externalReference` — if the first attempt succeeded, you will receive the conflict error; if it failed, the unique constraint was never recorded and you can safely retry.

<Info>
  If a booking creation fails with `422 BOOKING_CREATION_FAILED`, the `externalReference` was **not** saved. You can reuse it in a retry.
</Info>

***

### Failed Reference Save

In the rare scenario where a booking is created but the partner reference cannot be saved:

1. The system automatically marks the booking as `FAILED`.
2. The `externalReference` is not saved.
3. You can safely retry the create request with the same `externalReference`.

If the issue persists, contact support with the `requestId` from the error response.

***

## Booking Cancellation

Before cancelling via the Partner API, call `GET /partners/bookings/cancellation-reasons` and pass the selected `reasonId` in `PATCH /partners/bookings/:bookingId/cancel`.

### Refund Behavior on Cancellation

| Payment Responsibility | Refund Behavior                                                                                                                   |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `CLEANOS`              | Cancellation action is `CANCEL_AND_REFUND`. CleanLife initiates a refund automatically if applicable.                             |
| `PARTNER`              | Cancellation action is `CANCEL_ONLY`. No refund is initiated by CleanLife. Your system is responsible for refunding the customer. |

***

## Payment Confirmation

### Only PARTNER Responsibility Can Confirm

The `confirm-payment` endpoint is exclusively available to partners with `paymentResponsibility = PARTNER`. Attempting to call it with `CLEANOS` responsibility returns `422 PAYMENT_RESPONSIBILITY_MISMATCH`.

### One-Time Confirmation

Once payment is confirmed, it cannot be undone. Attempting to confirm again returns `422 PAYMENT_ALREADY_CONFIRMED`. This is by design — treat it as a safe idempotency check.

### Confirmation After Cancellation Is Blocked

You cannot confirm payment for a `canceled` or `failed` booking. Cancel the booking only after resolving any payment disputes with your customer.

***

## Timeslot Interpretation

All timeslot times are treated as **Asia/Riyadh local time** (UTC+3). Send times in `HH:MM` or `HH:MM:SS` format together with the service `date`.

If your systems operate in a different timezone, always convert to Riyadh local time before building booking requests.

***

## Domain Validation

Domain validation is a two-layer system:

1. **Allowed domain whitelist** — configured by the CleanLife team per partner.
2. **Request domain check** — if your partner account has configured domains and a request includes an `Origin` or `Referer` header, it is validated against the whitelist. Server-to-server requests (no `Origin`/`Referer`) bypass this check.

***

## Immutable Fields

Once set, the following fields **cannot** be changed:

| Field                       | Notes                                            |
| --------------------------- | ------------------------------------------------ |
| `partnerClientId`           | Your partner account ID; assigned at creation    |
| `bookingId`                 | Assigned at creation                             |
| `partnerPaymentConfirmedAt` | Set once by `confirm-payment`; cannot be cleared |
