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

# SDK Examples

> Complete code examples in cURL, JavaScript, TypeScript, Python, C#, and PHP.

Complete code examples for common Partner API operations across multiple languages.

***

## Environment Setup

All examples use the **Sandbox** environment by default. Set the following environment variables:

```bash theme={null}
CLEANLIFE_API_KEY=pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
CLEANLIFE_BASE_URL=https://apiv3.thecleanlife.dev/v1
```

For **Production**, change `CLEANLIFE_BASE_URL` to `https://api.cleanlife.sa/v1` and use your production API key.

***

## cURL

### Find or Create Contact

```bash theme={null}
curl -X POST "${CLEANLIFE_BASE_URL}/partners/contacts" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+966500000000",
    "name": "Ahmed Ali",
    "latitude": "24.7136",
    "longitude": "46.6753",
    "cityName": "Riyadh",
    "districtName": "Al Olaya",
    "streetName": "King Fahd Road"
  }'
```

### List Services

```bash theme={null}
curl -X GET "${CLEANLIFE_BASE_URL}/partners/services?addressId=bbbbbbbb-0000-0000-0000-000000000002&page=1&limit=50" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}"
```

### Create a Booking

```bash theme={null}
curl -X POST "${CLEANLIFE_BASE_URL}/partners/bookings" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "externalReference": "ORDER-20260615-001",
    "contactId": "aaaaaaaa-0000-0000-0000-000000000001",
    "addressId": "bbbbbbbb-0000-0000-0000-000000000002",
    "serviceId": "dddddddd-0000-0000-0000-000000000004",
    "date": "2026-06-20",
    "timeslot": {
      "startAt": "09:00",
      "endAt": "12:00"
    }
  }'
```

### Get Booking Status

```bash theme={null}
curl -X GET "${CLEANLIFE_BASE_URL}/partners/bookings/11111111-0000-0000-0000-000000000001/status" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}"
```

### List Cancellation Reasons

```bash theme={null}
curl -X GET "${CLEANLIFE_BASE_URL}/partners/bookings/cancellation-reasons" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}"
```

### Cancel a Booking

```bash theme={null}
curl -X PATCH "${CLEANLIFE_BASE_URL}/partners/bookings/11111111-0000-0000-0000-000000000001/cancel" \
  -H "x-api-key: ${CLEANLIFE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"reasonId": "aaaaaaaa-0000-0000-0000-000000000001", "notes": "Customer requested cancellation."}'
```

***

## JavaScript (fetch)

```javascript theme={null}
const BASE_URL = process.env.CLEANLIFE_BASE_URL;
const API_KEY = process.env.CLEANLIFE_API_KEY;

const headers = {
  'x-api-key': API_KEY,
  'Content-Type': 'application/json',
};

// Find or Create Contact
async function findOrCreateContact({
  phone,
  name,
  latitude,
  longitude,
  cityName,
  districtName,
  streetName,
}) {
  const response = await fetch(`${BASE_URL}/partners/contacts`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      phone,
      name,
      ...(latitude && longitude ? { latitude, longitude } : {}),
      ...(cityName && { cityName }),
      ...(districtName && { districtName }),
      ...(streetName && { streetName }),
    }),
  });
  const data = await response.json();
  if (!data.success) throw new Error(data.error.code + ': ' + data.error.message);
  return data.data; // { id, name, phone, addressId, isNewContact }
}

// Create Booking
async function createBooking(bookingData) {
  const response = await fetch(`${BASE_URL}/partners/bookings`, {
    method: 'POST',
    headers,
    body: JSON.stringify(bookingData),
  });
  const data = await response.json();
  if (!data.success) throw new Error(data.error.code + ': ' + data.error.message);
  return data.data;
}

// Get Booking Status
async function getBookingStatus(bookingId) {
  const response = await fetch(`${BASE_URL}/partners/bookings/${bookingId}/status`, { headers });
  const data = await response.json();
  if (!data.success) throw new Error(data.error.code + ': ' + data.error.message);
  return data.data;
}
```

***

## TypeScript (axios)

```typescript theme={null}
import axios, { AxiosInstance } from 'axios';

interface BookingResponse {
  bookingId: string;
  externalReference: string | null;
  status: string;
  paymentStatus: string;
  trackingReference: string;
  date: string;
  timeslot: { startAt: string; endAt: string; endsAtNextDay: boolean };
  appointment: null | { id: string; name: string; status: string; scheduledStartDateTime: string; scheduledEndDateTime: string };
}

interface PartnerApiSuccess<T> { success: true; data: T; }
interface PartnerApiError { success: false; error: { code: string; message: string; requestId: string }; }

class CleanLifePartnerClient {
  private client: AxiosInstance;

  constructor(apiKey: string, baseURL: string) {
    this.client = axios.create({
      baseURL,
      timeout: 30_000,
      headers: {
        'x-api-key': apiKey,
        'Content-Type': 'application/json',
      },
    });
  }

  async findOrCreateContact(data: {
    phone: string;
    name: string;
    latitude?: string;
    longitude?: string;
    cityName?: string;
    districtName?: string;
    streetName?: string;
  }): Promise<{
    id: string;
    name: string;
    phone: string;
    addressId: string | null;
    isNewContact: boolean;
  }> {
    const response = await this.client.post<
      PartnerApiSuccess<{
        id: string;
        name: string;
        phone: string;
        addressId: string | null;
        isNewContact: boolean;
      }>
    >('/partners/contacts', data);
    return response.data.data;
  }

  async createBooking(data: {
    externalReference?: string;
    contactId: string;
    addressId: string;
    serviceId: string;
    date: string;
    timeslot: { startAt: string; endAt: string };
  }): Promise<BookingResponse> {
    const response = await this.client.post<PartnerApiSuccess<BookingResponse>>('/partners/bookings', data);
    return response.data.data;
  }

  async getBookingStatus(bookingId: string): Promise<BookingResponse> {
    const response = await this.client.get<PartnerApiSuccess<BookingResponse>>(
      `/partners/bookings/${bookingId}/status`
    );
    return response.data.data;
  }

  async listCancellationReasons(): Promise<Array<{ id: string; nameEn: string; nameAr: string }>> {
    const response = await this.client.get<PartnerApiSuccess<Array<{ id: string; nameEn: string; nameAr: string }>>>(
      '/partners/bookings/cancellation-reasons',
    );
    return response.data.data;
  }

  async cancelBooking(bookingId: string, options: { reasonId: string; notes?: string }): Promise<BookingResponse> {
    const response = await this.client.patch<PartnerApiSuccess<BookingResponse>>(
      `/partners/bookings/${bookingId}/cancel`,
      options,
    );
    return response.data.data;
  }

  async confirmPayment(bookingId: string, paymentReference?: string): Promise<BookingResponse> {
    const response = await this.client.post<PartnerApiSuccess<BookingResponse>>(
      `/partners/bookings/${bookingId}/confirm-payment`,
      paymentReference ? { paymentReference } : {}
    );
    return response.data.data;
  }
}

export { CleanLifePartnerClient };
```

***

## Python (requests)

```python theme={null}
import os
import requests
from typing import Optional

class CleanLifePartnerClient:
    def __init__(self, api_key: str, base_url: str):
        self.base_url = base_url.rstrip('/')
        self.session = requests.Session()
        self.session.headers.update({
            'x-api-key': api_key,
            'Content-Type': 'application/json',
        })
        self.session.timeout = 30

    def find_or_create_contact(
        self,
        phone: str,
        name: str,
        *,
        latitude: Optional[str] = None,
        longitude: Optional[str] = None,
        city_name: Optional[str] = None,
        district_name: Optional[str] = None,
        street_name: Optional[str] = None,
    ) -> dict:
        body = {'phone': phone, 'name': name}
        if latitude and longitude:
            body['latitude'] = latitude
            body['longitude'] = longitude
        if city_name:
            body['cityName'] = city_name
        if district_name:
            body['districtName'] = district_name
        if street_name:
            body['streetName'] = street_name
        response = self.session.post(f'{self.base_url}/partners/contacts', json=body)
        response.raise_for_status()
        result = response.json()
        if not result['success']:
            raise Exception(f"{result['error']['code']}: {result['error']['message']}")
        return result['data']

    def create_booking(self, data: dict) -> dict:
        response = self.session.post(f'{self.base_url}/partners/bookings', json=data)
        response.raise_for_status()
        result = response.json()
        if not result['success']:
            raise Exception(f"{result['error']['code']}: {result['error']['message']}")
        return result['data']

    def get_booking_status(self, booking_id: str) -> dict:
        response = self.session.get(f'{self.base_url}/partners/bookings/{booking_id}/status')
        response.raise_for_status()
        result = response.json()
        if not result['success']:
            raise Exception(result['error']['code'])
        return result['data']

    def list_cancellation_reasons(self) -> list:
        response = self.session.get(f'{self.base_url}/partners/bookings/cancellation-reasons')
        response.raise_for_status()
        return response.json()['data']

    def cancel_booking(self, booking_id: str, reason_id: str, notes: Optional[str] = None) -> dict:
        body = {'reasonId': reason_id}
        if notes:
            body['notes'] = notes
        response = self.session.patch(f'{self.base_url}/partners/bookings/{booking_id}/cancel', json=body)
        response.raise_for_status()
        return response.json()['data']

    def confirm_payment(self, booking_id: str, payment_reference: Optional[str] = None) -> dict:
        body = {}
        if payment_reference:
            body['paymentReference'] = payment_reference
        response = self.session.post(
            f'{self.base_url}/partners/bookings/{booking_id}/confirm-payment',
            json=body
        )
        response.raise_for_status()
        return response.json()['data']


# Usage
client = CleanLifePartnerClient(
    api_key=os.environ['CLEANLIFE_API_KEY'],
    base_url=os.environ['CLEANLIFE_BASE_URL']
)

booking = client.create_booking({
    'externalReference': 'ORDER-20260615-001',
    'contactId': 'aaaaaaaa-0000-0000-0000-000000000001',
    'addressId': 'bbbbbbbb-0000-0000-0000-000000000002',
    'serviceId': 'dddddddd-0000-0000-0000-000000000004',
    'date': '2026-06-20',
    'timeslot': {'startAt': '09:00', 'endAt': '12:00'}
})
print(booking['bookingId'])
```

***

## C\#

```csharp theme={null}
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;

public class CleanLifePartnerClient
{
    private readonly HttpClient _httpClient;

    public CleanLifePartnerClient(string apiKey, string baseUrl)
    {
        _httpClient = new HttpClient { BaseAddress = new Uri(baseUrl), Timeout = TimeSpan.FromSeconds(30) };
        _httpClient.DefaultRequestHeaders.Add("x-api-key", apiKey);
    }

    public async Task<JsonElement> CreateBookingAsync(object bookingData)
    {
        var response = await _httpClient.PostAsJsonAsync("/partners/bookings", bookingData);
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<JsonElement>();
        if (!result.GetProperty("success").GetBoolean())
        {
            var error = result.GetProperty("error");
            throw new Exception($"{error.GetProperty("code").GetString()}: {error.GetProperty("message").GetString()}");
        }
        return result.GetProperty("data");
    }

    public async Task<JsonElement> GetBookingStatusAsync(string bookingId)
    {
        var response = await _httpClient.GetAsync($"/partners/bookings/{bookingId}/status");
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<JsonElement>();
        return result.GetProperty("data");
    }
}
```

***

## PHP

```php theme={null}
<?php

class CleanLifePartnerClient
{
    private string $baseUrl;
    private string $apiKey;

    public function __construct(string $apiKey, string $baseUrl)
    {
        $this->apiKey = $apiKey;
        $this->baseUrl = rtrim($baseUrl, '/');
    }

    private function request(string $method, string $path, array $body = null): array
    {
        $ch = curl_init($this->baseUrl . $path);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'x-api-key: ' . $this->apiKey,
            'Content-Type: application/json',
        ]);

        if ($method === 'POST' || $method === 'PATCH') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
            if ($body !== null) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
            }
        }

        $response = curl_exec($ch);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $data = json_decode($response, true);
        if (!$data['success']) {
            throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
        }
        return $data['data'];
    }

    public function findOrCreateContact(
        string $phone,
        string $name,
        ?string $latitude = null,
        ?string $longitude = null,
        ?string $cityName = null,
        ?string $districtName = null,
        ?string $streetName = null,
    ): array {
        $body = ['phone' => $phone, 'name' => $name];
        if ($latitude !== null && $longitude !== null) {
            $body['latitude'] = $latitude;
            $body['longitude'] = $longitude;
        }
        if ($cityName !== null) {
            $body['cityName'] = $cityName;
        }
        if ($districtName !== null) {
            $body['districtName'] = $districtName;
        }
        if ($streetName !== null) {
            $body['streetName'] = $streetName;
        }
        return $this->request('POST', '/partners/contacts', $body);
    }

    public function createBooking(array $bookingData): array
    {
        return $this->request('POST', '/partners/bookings', $bookingData);
    }

    public function getBookingStatus(string $bookingId): array
    {
        return $this->request('GET', "/partners/bookings/{$bookingId}/status");
    }

    public function listCancellationReasons(): array
    {
        return $this->request('GET', '/partners/bookings/cancellation-reasons');
    }

    public function cancelBooking(string $bookingId, string $reasonId, ?string $notes = null): array
    {
        $body = ['reasonId' => $reasonId];
        if ($notes !== null) {
            $body['notes'] = $notes;
        }
        return $this->request('PATCH', "/partners/bookings/{$bookingId}/cancel", $body);
    }

    public function confirmPayment(string $bookingId, string $paymentReference = null): array
    {
        $body = $paymentReference ? ['paymentReference' => $paymentReference] : [];
        return $this->request('POST', "/partners/bookings/{$bookingId}/confirm-payment", $body);
    }
}

// Usage
$client = new CleanLifePartnerClient(
    getenv('CLEANLIFE_API_KEY'),
    getenv('CLEANLIFE_BASE_URL')
);

$booking = $client->createBooking([
    'externalReference' => 'ORDER-20260615-001',
    'contactId'         => 'aaaaaaaa-0000-0000-0000-000000000001',
    'addressId'         => 'bbbbbbbb-0000-0000-0000-000000000002',
    'serviceId'         => 'dddddddd-0000-0000-0000-000000000004',
    'date'              => '2026-06-20',
    'timeslot'          => ['startAt' => '09:00', 'endAt' => '12:00'],
]);

echo $booking['bookingId'];
```
