Overview
Creates a new booking on the CleanLife platform on behalf of one of your customers. This is the core endpoint of the Partner API. The booking creation flow varies based on your partner account’spaymentResponsibility setting:
CLEANOSresponsibility: CleanLife handles payment. Depending on the service price and any coupons applied, the booking may transition towaiting payment(CleanLife sends a payment link asynchronously) or directly toin progress(free service or coupon covers the cost).PARTNERresponsibility: Your system handles payment. The booking is set toin progressimmediately, bypassing CleanLife payment processing. You are expected to confirm payment later viaPOST /partners/bookings/:bookingId/confirm-payment.
Endpoint
Authentication
Requires a valid API Key with thepartner_bookings_create permission.
Request Headers
| Header | Required | Value |
|---|---|---|
x-api-key | Yes | Your partner API key |
Content-Type | Yes | application/json |
Request Body
Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
contactId | UUID | Yes | ID of the customer contact. Obtain via POST /partners/contacts. |
addressId | string (UUID) | Yes | ID of the customer’s service address. Used to determine the service zone. |
serviceId | UUID | Yes | Service ID from GET /partners/services. Must be enabled for your partner account. The service category is resolved automatically — do not send categoryId. |
timeslot | object | Yes | Appointment timeslot. |
timeslot.startAt | string | Yes | Start time in HH:MM or HH:MM:SS format (Riyadh local time). |
timeslot.endAt | string | Yes | End time in HH:MM or HH:MM:SS format (Riyadh local time). |
date | ISO date string | Yes | Service date in YYYY-MM-DD format (Riyadh local date). |
externalReference | string | No | Your own reference ID for this booking (e.g., your internal order number). Must be unique per partner. Max 150 characters. |
packageId | UUID | No | Package ID (for bundled service offerings). |
customServices | array | No | Array of add-on custom services with optional quantities. |
customServices[].id | UUID | Yes (if array present) | ID of the custom service. |
customServices[].quantity | integer ≥ 1 | No | Quantity, defaults to 1. |
hours | integer ≥ 1 | No | Duration in hours. Defaults to 1. |
numberOfTeams | integer ≥ 2 | No | Number of cleaning teams. Defaults to 1 (implied). Only set if more than one team is needed; minimum is 2 when specified. |
workType | string (enum) | No | Type of work. See WorkType enum. |
userId | UUID | No | CleanLife platform user ID (for coupon validation purposes). |
couponIds | UUID[] | No | Up to 2 coupon IDs to apply. |
codes | string[] | No | Up to 2 coupon/promo codes to apply. |
promoCode | string | No | Promotional code (legacy field; prefer codes). |
contractCode | string | No | Contract code for enterprise/B2B pricing. |
discountCode | string | No | Discount code. |
Example Request
Success Response
HTTP Status:200 OK
Response Fields
| Field | Type | Description |
|---|---|---|
bookingId | UUID | CleanLife booking ID. Use this for all subsequent booking operations. |
externalReference | string / null | The externalReference you provided, or null if not provided. |
appointmentId | UUID / null | The service appointment ID, or null if not yet scheduled. |
status | string | Booking status. See Booking Statuses. |
paymentStatus | string | Payment status. See Payment Statuses. |
trackingReference | string | A human-readable reference (usually the service appointment name). Use this when communicating with your customers. |
date | date | The service date. |
timeslot.startAt | string | Start time of the appointment. |
timeslot.endAt | string | End time of the appointment. |
timeslot.endsAtNextDay | boolean | true if the appointment spans midnight. |
appointment | object / null | Appointment details if assigned, otherwise null. |
appointment.status | string | See Appointment Statuses. |
Booking Statuses
| Status | Description |
|---|---|
in progress | Booking is active and the service has been scheduled. |
waiting payment | Booking is pending payment from the customer (CLEANOS responsibility only). |
success | Service was completed successfully. |
canceled | Booking was cancelled. |
failed | Booking failed (payment or validation error). |
Payment Statuses
| Status | Description |
|---|---|
NOT_REQUIRED | No payment needed (free service or no-payment-model). |
PENDING | Payment has not yet been confirmed. For PARTNER responsibility: awaiting your confirm-payment call. For CLEANOS responsibility: awaiting customer payment. |
PAID | Payment has been confirmed (either CleanLife payment record exists, or partner called confirm-payment). |
FAILED | Payment process failed. |
CANCELLED | Booking was cancelled. |
Appointment Statuses
| Status | Description |
|---|---|
New | Appointment created, not yet scheduled. |
Scheduled | Appointment has been assigned to a team. |
Confirmed | Appointment confirmed. |
In The Way | Team is traveling to the location. |
In The location | Team has arrived. |
In Progress | Service is underway. |
Finished | Service work is done. |
Completed | Appointment fully completed. |
Cannot Complete | Team could not complete the service. |
Cancelled | Appointment was cancelled. |
Error Responses
| HTTP Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | One or more required fields are missing or invalid (e.g. missing serviceId) |
401 | UNAUTHORIZED | Invalid or missing API key |
403 | FORBIDDEN | Missing partner_bookings_create permission |
404 | RESOURCE_NOT_FOUND / SERVICE_NOT_ALLOWED | serviceId not found or not enabled for your partner account |
409 | DUPLICATE_EXTERNAL_REFERENCE | The externalReference is already used by another booking under your account |
422 | BOOKING_CREATION_FAILED | The booking could not be completed — typically a payment or validation error |
Business Rules
-
externalReferenceuniqueness: If you provide anexternalReference, it must be unique across all bookings for your partner account. Duplicate values result in a409error. This is your primary tool for preventing duplicate bookings — generate a unique ID on your side before calling the API. -
serviceIdis required. Choose aserviceIdfromGET /partners/services. Do not sendcategoryId— the platform resolves the service category automatically. -
Timeslot interpretation. All timeslot times (
startAt,endAt) are interpreted as Asia/Riyadh local time (UTC+3). -
Payment flow — CLEANOS responsibility:
- If the booking total is zero (free service or full coupon), it goes directly to
in progress. - If the booking requires payment, CleanLife sends a payment link asynchronously. The booking enters
waiting payment. Abooking.payment_failedwebhook is sent if the async payment link fails.
- If the booking total is zero (free service or full coupon), it goes directly to
-
Payment flow — PARTNER responsibility:
- The booking is immediately set to
in progress. - You must call
POST /partners/bookings/:bookingId/confirm-paymentonce you collect payment from your customer.
- The booking is immediately set to
-
Failed reference save. In the rare event that a booking is created but the partner reference cannot be saved, the booking is marked as
FAILEDso you can safely retry with the sameexternalReference. -
Webhook on creation. A
booking.createdwebhook event is fired immediately after successful booking creation.
Notes
- The
appointmentIdmay benullimmediately after creation in some scheduling flows. Monitor thebooking.appointment_status_changedwebhook event. - The
trackingReferenceis the value you should display to your customers for tracking purposes. - Do not rely on the
appointmentobject being present at creation time.
Integration Tips
- Always use
externalReferenceto tie CleanLife bookings back to your internal orders. Without it, deduplication on retry is much harder. - Obtain
contactIdfirst viaPOST /partners/contactsbefore creating a booking. - Verify available timeslots first using
GET /partners/timeslots/availablebefore creating a booking. - Calculate the expected price first using
POST /partners/pricing/calculate-priceto avoid surprises.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Not providing externalReference | Cannot deduplicate retries | Always generate and pass a unique reference |
Using HH:MM:SS vs HH:MM inconsistently | Both formats are accepted | Use HH:MM for consistency |
| Passing a date in UTC instead of Riyadh local | Booking created on wrong date | Always use YYYY-MM-DD in Riyadh local time |
Reusing externalReference after a failed booking | 409 DUPLICATE_EXTERNAL_REFERENCE if the failed booking reference was saved | The system compensates failed orphaned bookings; if you receive 422 BOOKING_CREATION_FAILED, the reference is not saved and you can retry |
Related Endpoints
POST /partners/contacts— Get or createcontactIdGET /partners/timeslots/available— Get available timeslots before bookingPOST /partners/pricing/calculate-price— Preview the booking priceGET /partners/bookings/:bookingId/status— Poll booking statusPATCH /partners/bookings/:bookingId— Update the booking date/timeslotPATCH /partners/bookings/:bookingId/cancel— Cancel the bookingPOST /partners/bookings/:bookingId/confirm-payment— Confirm payment (PARTNER responsibility only)