Skip to main content

Overview

The Partner API uses API Key authentication. Every request must carry a valid API Key issued by CleanLife. There is no OAuth flow, no JWT, and no session management required. The API Key acts as both your identity credential and your permission token. It is validated on every request.

How Authentication Works

  1. Include your API Key in the x-api-key request header on every request.
  2. The platform verifies that the key is valid, active, and associated with your partner account.
  3. If an IP allowlist is configured for your key, requests from other addresses are rejected.
  4. Your request rate is checked against your configured limit (if any).
  5. If a domain allowlist is configured on your account, browser-originated requests are validated against your registered domains.
  6. The platform checks that your key has the permission required for the endpoint you are calling.
  7. If all checks pass, your request proceeds.

Required Header

HeaderDescription
x-api-keyYour partner API key. Required on every request.
The key can also be passed as a query parameter api_key, but this is strongly discouraged because query parameters appear in server logs, proxy logs, and browser history.

API Key Format

API Keys are opaque strings generated by the platform. The exact format is internal and may vary. Do not parse or derive meaning from the key value.

Authentication Examples

cURL

curl -X GET "https://apiv3.thecleanlife.dev/v1/partners/services" \
  -H "x-api-key: YOUR_API_KEY_HERE"

JavaScript (Node.js / fetch)

const response = await fetch('https://apiv3.thecleanlife.dev/v1/partners/services', {
  method: 'GET',
  headers: {
    'x-api-key': process.env.CLEANLIFE_API_KEY,
    'Content-Type': 'application/json',
  },
});
const data = await response.json();

Python (requests)

import requests

response = requests.get(
    'https://apiv3.thecleanlife.dev/v1/partners/services',
    headers={
        'x-api-key': 'YOUR_API_KEY_HERE',
        'Content-Type': 'application/json',
    }
)
data = response.json()

TypeScript (axios)

import axios from 'axios';

const client = axios.create({
  baseURL: 'https://apiv3.thecleanlife.dev/v1',
  headers: {
    'x-api-key': process.env.CLEANLIFE_API_KEY!,
    'Content-Type': 'application/json',
  },
});

const { data } = await client.get('/partners/services');

C#

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");

var response = await client.GetAsync("https://apiv3.thecleanlife.dev/v1/partners/services");
var json = await response.Content.ReadAsStringAsync();

PHP

<?php
$ch = curl_init("https://apiv3.thecleanlife.dev/v1/partners/services");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'x-api-key: YOUR_API_KEY_HERE',
    'Content-Type: application/json',
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);

Permissions Model

Each API Key is granted a set of permissions. If you attempt to call an endpoint without the required permission, you will receive a 403 Forbidden response.
PermissionEndpoints
partner_bookings_createPOST /partners/bookings
partner_bookings_readGET /partners/bookings, GET /partners/bookings/:id/status
partner_bookings_updatePATCH /partners/bookings/:id
partner_bookings_cancelPATCH /partners/bookings/:id/cancel
partner_bookings_confirm_paymentPOST /partners/bookings/:id/confirm-payment
partner_services_readGET /partners/services, GET /partners/services/:id
partner_pricing_readGET /partners/pricing/tiers, POST /partners/pricing/calculate-price
partner_timeslots_readGET /partners/timeslots/available
partner_webhooks_manageAll /partners/webhooks/* endpoints
partner_contacts_readPOST /partners/contacts, GET /partners/contacts/:contactId
Request only the permissions you actually need. Following the principle of least privilege reduces risk if your key is ever compromised.

Domain Allowlist (Optional)

If your partner account has allowed webhook domains configured, the platform will additionally validate that incoming requests originate from one of your registered domains. Domain detection happens in the following priority order:
  1. Origin request header
  2. Referer request header
  3. x-partner-domain request header (optional fallback)
Behavior:
ScenarioResult
Partner has no domains configuredNo domain check — all requests pass
Partner has domains configured, no domain detected in requestPasses (server-to-server calls)
Partner has domains configured, domain detected, matchesPasses
Partner has domains configured, domain detected, no match403 INVALID_PARTNER_DOMAIN
Matched domain is a sandbox domain403 SANDBOX_DOMAIN_NOT_ALLOWED (currently disabled)
This means server-to-server integrations (where there is no Origin or Referer header) are never blocked by the domain check, even when domains are configured.

Authentication Errors

HTTP StatusError CodeCause
401 UnauthorizedUNAUTHORIZEDAPI key is missing, invalid, or belongs to a suspended partner
403 ForbiddenFORBIDDENAPI key is valid but does not have the required permission
403 ForbiddenINVALID_PARTNER_DOMAINRequest domain is not in your partner’s allowlist
403 ForbiddenSANDBOX_DOMAIN_NOT_ALLOWEDRequest came from a sandbox domain (currently disabled globally)
429 Too Many RequestsRATE_LIMIT_EXCEEDEDRequests exceeded your rate limit within the current window

Security Recommendations

  1. Never expose your API Key client-side. Store it as a server-side environment variable (e.g., CLEANLIFE_API_KEY).
  2. Rotate your key periodically. Contact the CleanLife team to issue a new key.
  3. Use HTTPS exclusively. Never call the API over plain HTTP.
  4. Log and monitor failed authentication responses (401, 403) to detect unauthorized usage.
  5. IP allowlist — ask the CleanLife team to restrict your API Key to specific egress IP addresses of your servers.
  6. Scope permissions carefully — request only the permissions your integration needs.