Aller au contenu principal

    Documentation développeurs · v1

    API CleanPlus : connectez votre tunnel de réservation à votre logiciel de nettoyage

    Proposez sur votre site les vrais créneaux libres de vos équipes, créez automatiquement le client, le site et l'intervention dans CleanPlus, et recevez en temps réel chaque modification faite dans le planning. API REST JSON, authentification par clé, spécification OpenAPI importable dans Postman, Insomnia ou votre générateur de SDK.

    REST + JSON
    Clé par organisation
    Idempotence native
    Webhooks signés
    Sommaire

    Démarrage rapide en 5 minutes

    1. Créez une clé : un administrateur ouvre Paramètres → Développeurs & API, clique « Nouvelle clé » et copie la valeur cp_live_… (affichée une seule fois).
    2. Stockez-la côté serveur dans une variable d'environnement CLEANPLUS_KEY.
    3. Testez l'appel ci-dessous : vous devez recevoir la liste de vos prestations.
    4. Déclarez un webhook https dans le même écran et cliquez « Tester » pour valider la réception.
    bash
    curl https://cleanplus.app/api/public/v1/services -H "Authorization: Bearer $CLEANPLUS_KEY"

    Parcours type d'un tunnel de réservation :

    flux
    1. GET  /availability   → afficher les créneaux libres pour la durée estimée
    2. POST /bookings       → à la validation : client + site + intervention créés
    3. Webhooks             → booking.rescheduled / cancelled / intervention.completed
    4. PATCH ou /cancel     → modification ou annulation demandée par le client

    Authentification & droits

    Chaque requête porte la clé dans l'en-tête Authorization. Les clés sont propres à une organisation : toutes les données lues ou créées appartiennent à cette organisation. Elles sont stockées chiffrées (empreinte SHA-256) et révocables à tout moment. N'exposez jamais une clé dans le navigateur ou une application mobile : appelez l'API depuis votre serveur.

    http
    Authorization: Bearer cp_live_xxxxxxxxxxxxxxxxxxxxxxxx
    DroitAutorise
    availability:readLire les créneaux libres et le catalogue des prestations
    bookings:writeCréer, modifier, reprogrammer et annuler des réservations
    bookings:readConsulter une réservation

    Bonnes pratiques : une clé par intégration, droits minimum nécessaires, rotation en cas de départ d'un prestataire.

    Conventions

    • URL de base : https://cleanplus.app/api/public/v1 — HTTPS uniquement.
    • Format : JSON UTF-8, en-tête Content-Type: application/json pour POST et PATCH.
    • Fuseau horaire : dates AAAA-MM-JJ et heures HH:MM exprimées à l'heure de Paris (Europe/Paris) ; horodatages techniques en ISO 8601 UTC.
    • Idempotence : POST /bookings exige Idempotency-Key (ex. l'id de votre commande). Rejouer la même clé renvoie la même réservation, sans doublon — idéal après un timeout réseau.
    • Limite de débit : 120 requêtes par minute et par clé ; au-delà, réponse 429.
    • CORS : autorisé, mais l'usage serveur est recommandé pour protéger la clé.
    • Compatibilité : des champs peuvent être ajoutés aux réponses sans changer de version ; ignorez les champs inconnus.

    Endpoints

    Créneaux libres

    GET/availabilityscope : availability:read

    Renvoie les créneaux où au moins agents_count agents actifs sont libres pendant toute la durée demandée, dans les heures d'ouverture (8h–19h), en tenant compte des interventions déjà planifiées, des absences et d'un temps de trajet de 30 minutes entre deux interventions. Les créneaux passés sont exclus.

    Paramètres de requête

    ChampTypeDescription
    date_fromrequisdatePremier jour recherché (AAAA-MM-JJ).
    date_torequisdateDernier jour inclus. Plage de 31 jours maximum.
    duration_minutesrequisintegerDurée estimée de la prestation, de 15 à 720 minutes.
    agents_countintegerNombre d'agents simultanés, de 1 à 10 (défaut 1).
    lat / lngnumberPosition du lieu d'intervention (enregistrée, non utilisée pour le tri en v1).
    curl "https://cleanplus.app/api/public/v1/availability?date_from=2026-10-12&date_to=2026-10-16&duration_minutes=120&agents_count=1" \
      -H "Authorization: Bearer $CLEANPLUS_KEY"
    200 OK
    {
      "timezone": "Europe/Paris",
      "slots": [
        { "date": "2026-10-12", "start_time": "08:00", "end_time": "10:00", "available_agents_count": 3 },
        { "date": "2026-10-12", "start_time": "08:30", "end_time": "10:30", "available_agents_count": 3 }
      ]
    }

    Catalogue des prestations

    GET/servicesscope : availability:read

    Liste les prestations actives de l'organisation avec leur prix unitaire HT, pour alimenter votre simulateur de tarif.

    200 OK
    {
      "services": [
        { "id": "…", "name": "Ménage complet", "description": "Toutes pièces", "category": "Particuliers",
          "unit_price_ht": 32.5, "unit": "heure" }
      ]
    }

    Créer une réservation

    POST/bookingsscope : bookings:write

    En un seul appel : le client est retrouvé (par e-mail puis SIRET) ou créé — ses informations existantes ne sont jamais modifiées —, le site est retrouvé par adresse ou créé, puis l'intervention est planifiée et les agents disponibles sont affectés automatiquement. Elle apparaît dans le planning avec le badge « Réservation en ligne ». Le créneau est revérifié au moment de la création.

    En-têtes

    ChampTypeDescription
    Idempotency-KeyrequisstringIdentifiant unique de la commande (≤ 200 caractères).

    Corps JSON

    ChampTypeDescription
    external_refstring ≤120Votre identifiant de commande, renvoyé dans chaque réponse et webhook.
    customer.typeenumparticulier (défaut) ou professionnel.
    customer.namerequisstring ≤200Nom de famille ou raison sociale.
    customer.first_namestring ≤100Prénom.
    customer.company_namestring ≤200Société (clients professionnels).
    customer.emailrequisemail ≤255Sert à retrouver un client existant (jamais écrasé).
    customer.phonestring ≤30Téléphone.
    customer.siret14 chiffresRapprochement des clients professionnels.
    site.addressrequisstring 3–300Adresse d'intervention. Un site existant à la même adresse est réutilisé.
    site.namestring ≤200Nom du site (défaut : client + adresse).
    site.postal_code / citystringCode postal (≤10) et ville (≤100).
    site.latitude / longitudenumberCoordonnées GPS.
    site.access_instructionsstring ≤2000Consignes d'accès visibles par les agents.
    site.access_codesstring ≤500Digicode, boîte à clés…
    site.surfacestring ≤50Surface, ex. « 85 m² ».
    site.premises_typestring ≤100Appartement, bureaux, local commercial…
    slot.daterequisdateJour choisi (AAAA-MM-JJ, heure de Paris).
    slot.start_timerequisHH:MMHeure de début, issue de /availability.
    slot.duration_minutesrequisinteger15 à 720 minutes. Doit correspondre à la recherche de créneaux.
    agents_countinteger1 à 10 (défaut 1).
    servicestring ≤200Prestation commandée (nom libre ou issu de /services).
    notesstring ≤5000Options, précisions, résultat du simulateur.
    attachments[]array ≤20Photos/vidéos : { url (https, ≤2000), name? }. Ajoutées aux documents du site.
    curl -X POST https://cleanplus.app/api/public/v1/bookings \
      -H "Authorization: Bearer $CLEANPLUS_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: CMD-1042" \
      -d '{
        "external_ref": "CMD-1042",
        "customer": { "type": "particulier", "name": "Martin", "first_name": "Julie", "email": "julie@exemple.fr", "phone": "0601020304" },
        "site": { "address": "12 rue de la Paix", "postal_code": "75002", "city": "Paris",
                  "access_instructions": "Digicode au portail, 3e étage", "surface": "85 m²" },
        "slot": { "date": "2026-10-12", "start_time": "08:00", "duration_minutes": 120 },
        "service": "Ménage complet",
        "notes": "Options : vitres, four",
        "attachments": [{ "url": "https://votre-stockage.fr/photo-salon.jpg", "name": "Salon" }]
      }'
    201 Created
    {
      "id": "5d0c6e0a-2c41-4a3e-9f1b-7f3b2d1e9a10",
      "external_ref": "CMD-1042",
      "source": "api",
      "status": "planifie",
      "date": "2026-10-12",
      "start_time": "08:00",
      "end_time": "10:00",
      "service": "Ménage complet",
      "notes": "Options : vitres, four",
      "site": { "id": "…", "name": "Martin — 12 rue de la Paix", "address": "12 rue de la Paix, 75002 Paris",
                "access_instructions": "Digicode au portail, 3e étage" },
      "customer": { "id": "…", "name": "Martin", "first_name": "Julie",
                    "email": "julie@exemple.fr", "phone": "0601020304" },
      "agents": [ { "id": "…", "role": "principal", "name": "Sofia Benali" } ],
      "created_at": "2026-10-01T14:02:11.000Z",
      "updated_at": "2026-10-01T14:02:11.000Z"
    }

    Réponses possibles : 201 créée (aussi renvoyé à l'identique en cas de rejeu idempotent), 400, 401, 403, 409 slot_unavailable, 429.

    Lire une réservation

    GET/bookings/{id}scope : bookings:read

    Renvoie l'objet Booking à jour (statut, horaires, agents affectés).

    bash
    curl https://cleanplus.app/api/public/v1/bookings/5d0c6e0a-2c41-4a3e-9f1b-7f3b2d1e9a10 -H "Authorization: Bearer $CLEANPLUS_KEY"

    Modifier ou reprogrammer

    PATCH/bookings/{id}scope : bookings:write

    Tous les champs sont facultatifs ; seuls ceux envoyés sont modifiés. Impossible sur une réservation réalisée ou annulée (409 booking_locked).

    Corps JSON

    ChampTypeDescription
    slotobject{ date, start_time, duration_minutes } — reprogramme et réattribue les agents (409 si indisponible).
    servicestring ≤200Nouvelle prestation.
    notesstring ≤5000Remplace les notes.
    access_instructionsstring ≤2000Met à jour les consignes du site.
    access_codesstring ≤500Met à jour les codes d'accès.
    bash
    curl -X PATCH https://cleanplus.app/api/public/v1/bookings/{id} \
      -H "Authorization: Bearer $CLEANPLUS_KEY" -H "Content-Type: application/json" \
      -d '{ "slot": { "date": "2026-10-13", "start_time": "14:00", "duration_minutes": 120 } }'

    Annuler une réservation

    POST/bookings/{id}/cancelscope : bookings:write

    Passe l'intervention au statut annule et libère les agents. Le motif est conservé dans les notes.

    Corps JSON

    ChampTypeDescription
    reasonstring ≤1000Motif d'annulation.
    bash
    curl -X POST https://cleanplus.app/api/public/v1/bookings/{id}/cancel \
      -H "Authorization: Bearer $CLEANPLUS_KEY" -H "Content-Type: application/json" \
      -d '{ "reason": "Annulé par le client" }'

    Objets

    Booking

    ChampTypeDescription
    idrequisuuidIdentifiant CleanPlus de la réservation.
    external_refrequisstring | nullVotre référence.
    sourcerequisapi | appOrigine de la réservation.
    statusrequisenumplanifie, en_cours, termine ou annule.
    daterequisdateJour d'intervention.
    start_time / end_timerequisHH:MMHoraires, heure de Paris.
    service / notesrequisstring | nullPrestation et notes.
    siterequisobject{ id, name, address, access_instructions }.
    customerrequisobject{ id, name, first_name, email, phone }.
    agents[]requisarray{ id, role, name } des agents affectés.
    created_at / updated_atrequisdatetimeHorodatages ISO 8601 (UTC).

    Slot

    ChampTypeDescription
    daterequisdateJour.
    start_time / end_timerequisHH:MMDébut et fin, heure de Paris.
    available_agents_countrequisintegerAgents libres sur toute la durée.

    Service

    ChampTypeDescription
    idrequisuuidIdentifiant.
    namerequisstringNom.
    description / categoryrequisstring | nullDétails.
    unit_price_htrequisnumber | nullPrix unitaire HT en euros.
    unitrequisstring | nullheure, mission, forfait…

    Error

    json
    { "error": { "code": "validation_error", "message": "Données invalides",
                 "details": { "slot.start_time": ["Format HH:MM attendu"] } } }

    Webhooks

    Déclarez une ou plusieurs adresses https dans Paramètres → Développeurs & API et choisissez les événements. Ils sont émis pour les réservations en ligne, que le changement vienne de l'API ou du planning CleanPlus.

    ÉvénementDéclencheur
    booking.createdRéservation créée (via l'API)
    booking.updatedConsignes, prestation ou notes modifiées
    booking.rescheduledDate ou horaire déplacé (API ou planning CleanPlus)
    booking.cancelledRéservation annulée (API ou planning)
    intervention.completedIntervention marquée réalisée par l'équipe

    Requête envoyée

    http
    POST https://votre-site.fr/webhooks/cleanplus
    Content-Type: application/json
    User-Agent: CleanPlus-Webhooks/1.0
    X-CleanPlus-Event: booking.rescheduled
    X-CleanPlus-Delivery: 8f1c…        ← identifiant unique, à utiliser pour dédoublonner
    X-CleanPlus-Signature: t=1791712320,v1=5f2b…
    
    { "id": "8f1c…", "type": "booking.rescheduled", "created_at": "2026-10-10T09:12:00Z",
      "data": { …objet Booking… } }

    Vérifier la signature

    HMAC-SHA256 de `${t}.${corps brut}` avec le secret du webhook. Refusez les messages de plus de 5 minutes.

    import crypto from "node:crypto";
    
    export function verify(rawBody, header, secret) {
      const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
      if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
      const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
      return expected.length === v1.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
    }

    Relances et fiabilité

    • Répondez par un code 2xx en moins de 10 secondes ; traitez ensuite de façon asynchrone.
    • En cas d'échec : 5 nouvelles tentatives (1 min, 5 min, 30 min, 2 h, 6 h).
    • Après 20 échecs consécutifs, l'adresse est désactivée et un administrateur est prévenu.
    • Un même événement peut arriver deux fois : dédoublonnez avec X-CleanPlus-Delivery.
    • L'ordre n'est pas garanti : comparez data.updated_at avant d'écraser vos données.

    Codes d'erreur

    HTTPCodeCauseAction
    400validation_errorUn champ est absent ou invalide (détail dans error.details)Corrigez la requête, ne pas rejouer telle quelle
    400invalid_jsonLe corps n'est pas un JSON valideVérifiez Content-Type et sérialisation
    400idempotency_key_requiredEn-tête Idempotency-Key absent sur POST /bookingsAjoutez un identifiant unique de commande
    401invalid_api_keyClé absente, erronée ou révoquéeVérifiez la clé dans Paramètres → Développeurs & API
    403insufficient_scopeLa clé n'a pas le droit requisGénérez une clé avec le bon droit
    404not_foundRéservation inexistante ou d'une autre organisationVérifiez l'identifiant
    409slot_unavailableLe créneau a été pris entre-tempsRechargez /availability et proposez un autre créneau
    409booking_lockedRéservation déjà réalisée ou annuléeAucune modification possible
    429rate_limitedPlus de 120 requêtes par minuteAttendez puis réessayez avec un délai croissant
    500internal_errorErreur inattendue côté CleanPlusRéessayez ; contactez le support si cela persiste

    FAQ intégrateurs

    Comment tester sans polluer le planning ?

    Créez une réservation à une date lointaine avec un external_ref du type TEST-…, vérifiez les webhooks, puis annulez-la via /cancel. Vous pouvez aussi révoquer la clé de test ensuite.

    Que faire en cas de 409 slot_unavailable ?

    Le créneau a été pris entre l'affichage et la validation. Rechargez /availability et proposez immédiatement les créneaux voisins à votre client.

    Le client existe déjà dans CleanPlus : est-il dupliqué ?

    Non. Il est retrouvé par e-mail puis par SIRET, et ses informations ne sont jamais modifiées par l'API.

    Comment transmettre photos et vidéos ?

    Hébergez-les chez vous (URL https accessible) et passez-les dans attachments : elles sont rattachées aux documents du site, visibles par l'équipe.

    Le prix est-il calculé par l'API ?

    Non en v1 : votre simulateur calcule le prix (à partir de /services si besoin) et vous pouvez l'indiquer dans notes. Le paiement en ligne reste géré par votre tunnel.

    Puis-je appeler l'API depuis le navigateur ?

    Techniquement oui (CORS ouvert), mais la clé serait exposée. Passez par votre serveur ou une fonction serverless.

    Versions

    • v1.0 — octobre 2026 : créneaux libres, prestations, réservations (création, lecture, modification, annulation), webhooks signés, OpenAPI 3.1.
    • Toute évolution incompatible fera l'objet d'une nouvelle version (/v2) ; la v1 restera disponible.

    Une question technique ? Écrivez à contact@cleanplus.app.

    Prêt à digitaliser votre entreprise de propreté ?

    Essai gratuit 14 jours, sans engagement. Mise en route en moins de 10 minutes.