Démarrer
L'API est réservée aux comptes entreprise (conciergeries, agences, loueurs en société). Tout se passe en trois étapes.
- Dans votre espace Groomly, onglet Développeur (en bas du menu, comptes entreprise), créez une clé. Elle s'affiche une seule fois : copiez-la dans votre logiciel.
- Appelez
GET /api/v1/compteavec cette clé pour vérifier qu'elle fonctionne. - Réservez un ménage avec
POST /api/v1/menages, puis abonnez une adresse de webhook pour suivre la mission sans interroger l'API.
Adresse de base : https://groomly-app.com/api/v1. Tout est en JSON, en UTF-8, en HTTPS uniquement. Les dates sont au format ISO 8601, en heure de Paris ; les montants sont en euros TTC.
curl https://groomly-app.com/api/v1/compte \
-H "Authorization: Bearer gk_live_…"
{
"donnees": {
"email": "contact@ma-conciergerie.fr",
"nom": "Ma Conciergerie",
"typeCompte": "PRO",
"facturationMensuelle": true,
"cle": { "id": 3, "nom": "StayCore", "prefixe": "gk_live_ab12cd34", "portee": "ECRITURE", "active": true },
"documentation": "https://groomly-app.com/developpeurs"
}
}
Authentification
Clé API
Chaque appel porte l'en-tête Authorization: Bearer <clé>. Une clé commence par gk_live_, fait 256 bits aléatoires, et n'est stockée chez Groomly que sous forme d'empreinte : personne, pas même Groomly, ne peut la relire. Elle agit strictement au nom du compte qui l'a créée.
Une clé a une portée : ECRITURE (par défaut, elle réserve et annule) ou LECTURE (elle consulte seulement). Un appel en écriture avec une clé en lecture reçoit 403 PORTEE_INSUFFISANTE.
Depuis l'espace Groomly, le compte peut limiter une clé à des adresses IP (adresses ou plages CIDR, IPv4 et IPv6) : un appel venu d'ailleurs reçoit 403 IP_NON_AUTORISEE. L'adresse retenue est celle de la sortie Internet de votre serveur ; si votre logiciel passe par un mandataire, déclarez l'adresse de sortie de celui-ci. Le compte consulte aussi, pour chaque clé, le journal de ses 50 derniers appels (date, route, résultat, adresse, durée) et le nombre d'appels des 24 dernières heures ; le journal est conservé 90 jours.
403 HTTPS_REQUIS) : la clé n'y transiterait pas chiffrée.Jeton court
Pour ne pas présenter la clé à chaque appel, échangez-la contre un jeton valable une heure. Le jeton s'utilise exactement comme la clé. Quand il expire, demandez-en un nouveau avec la clé : il n'y a pas de « refresh token » à conserver, la clé joue ce rôle. Révoquer la clé révoque immédiatement tous les jetons émis avec elle.
curl -X POST https://groomly-app.com/api/v1/jetons \
-H "Authorization: Bearer gk_live_…"
{ "donnees": { "jeton": "eyJ…", "type": "Bearer", "expireDans": 3600, "expireLe": "2026-10-05T16:12:00+02:00" } }
Rotation et révocation
Depuis l'onglet Développeur, Régénérer crée une nouvelle clé et laisse l'ancienne valable 24 heures : le temps de remplacer la clé chez votre intégrateur sans aucune interruption. Révoquer coupe une clé sur-le-champ. Un compte peut avoir cinq clés actives au plus, par exemple une par logiciel : révoquer l'une ne touche pas les autres.
Conventions
Les réponses réussies enveloppent le résultat dans donnees. Une création renvoie 201, une lecture ou une mise à jour 200.
Erreurs
Une erreur renvoie un statut HTTP 4xx ou 5xx et un corps { "erreur": { "code", "message" } }. Le code est stable et sert à votre logique ; le message, en français, s'affiche à un humain.
| Statut | Codes | Sens |
|---|---|---|
| 400 | DATE_INVALIDE, DATE_PASSEE, HEURE_INVALIDE, PRESTATION_INCONNUE, LOGEMENT_REQUIS, URL_INVALIDE… | La demande est mal formée. |
| 401 | NON_AUTHENTIFIE, CLE_INVALIDE, JETON_INVALIDE | Clé absente, inconnue, révoquée ou expirée. |
| 402 | PAIEMENT_REQUIS, PAIEMENT_REFUSE | Le compte n'a pas de moyen de paiement automatique (facturation mensuelle ou carte enregistrée), ou la banque a refusé. |
| 403 | PORTEE_INSUFFISANTE, HTTPS_REQUIS, IP_NON_AUTORISEE, COMPTE_PRO_INCOMPLET, MANDAT_PROPRIETAIRE | L'appel est compris mais pas permis dans cet état. |
| 404 | LOGEMENT_INTROUVABLE, MENAGE_INTROUVABLE, WEBHOOK_INTROUVABLE | L'objet n'existe pas sur ce compte. |
| 409 | DEJA_ANNULE, STATUT_NON_ANNULABLE, DELAI_DEPASSE, PAIEMENT_EN_COURS, TROP_DE_CLES | Conflit avec l'état actuel. |
| 429 | TROP_D_APPELS | Limite de débit atteinte : respectez Retry-After. |
| 503 | PAIEMENT_INDISPONIBLE | Le service de paiement ne répond pas : réessayez. |
Limites de débit
300 appels par minute et par clé. Au-delà, 429 avec un en-tête Retry-After en secondes. Les tentatives d'authentification ratées sont limitées à 30 par minute et par adresse.
Idempotence
À la création d'un ménage, donnez la référence de la réservation dans votre système, dans le champ reference ou dans l'en-tête Idempotency-Key. Si l'appel est rejoué (coupure réseau, relance), Groomly renvoie le ménage déjà créé avec 200 au lieu d'en créer un second. Par sécurité, un second ménage le même jour pour le même logement est également refusé en doublon.
Référence
Compte
Le compte de la clé, son mode de facturation et la clé elle-même (sans sa valeur). Idéal pour valider une clé que l'utilisateur vient de coller.
Un jeton d'une heure contre la clé (voir Jeton court).
Logements
Tous les logements du compte, du plus récent au plus ancien. C'est l'identifiant id qu'il faut pour réserver.
{
"donnees": [{
"id": 42, "nom": "Studio du lac", "type": "airbnb",
"adresse": { "rue": "12 quai Perrière", "codePostal": "74000", "ville": "Annecy" },
"surface": 45, "prestations": ["menage_courant", "linge"], "litsDouble": 1, "litsSimple": 1,
"tarifEstime": 81.0, "calendrier": { "actif": false, "etat": null }, "dateCreation": "2026-09-02T10:14:00+02:00"
}]
}
Tarifs
La grille publique : le ménage courant par tranche de surface (prix et durée), les options avec leur prix, le supplément d'urgence. De quoi afficher des cases à cocher avec le tarif dans votre logiciel. Prix en euros TTC ; linge se compte par lit déclaré sur le logement, grand_menage est un pourcentage de la base, urgence ne se demande pas : Groomly l'ajoute d'après la date.
{
"donnees": {
"devise": "EUR",
"menageCourant": [
{ "surfaceMin": 1, "surfaceMax": 25, "prix": 35.0, "dureeHeures": 1.0 },
{ "surfaceMin": 26, "surfaceMax": 40, "prix": 50.0, "dureeHeures": 1.5 },
{ "surfaceMin": 41, "surfaceMax": 60, "prix": 65.0, "dureeHeures": 2.0 },
{ "surfaceMin": 61, "surfaceMax": 80, "prix": 85.0, "dureeHeures": 2.5 },
{ "surfaceMin": 81, "surfaceMax": 100, "prix": 100.0, "dureeHeures": 3.0 },
{ "surfaceMin": 101, "surfaceMax": null, "prixParM2": 1.0, "surDevis": true }
],
"options": [
{ "cle": "vitres", "libelle": "Vitres, baies vitrées et miroirs",
"unite": "forfait", "prix": 10.0 },
{ "cle": "linge", "libelle": "Kit linge et lits faits",
"unite": "par lit", "prixLitDouble": 10.0, "prixLitSimple": 6.0 },
{ "cle": "repassage", "libelle": "Repassage du linge de maison (30 min)",
"unite": "forfait", "prix": 10.0 },
{ "cle": "exterieur", "libelle": "Terrasse, balcon, mobilier extérieur",
"unite": "forfait", "prix": 6.0 },
{ "cle": "grand_menage", "libelle": "Grand ménage, nettoyage en profondeur",
"unite": "pourcentage de la base", "taux": 0.3 }
],
"urgence": { "cle": "urgence", "libelle": "Intervention sous 48 h",
"prix": 10.0, "automatique": true }
}
}
Le devis exact d'un ménage pour ce logement, sans rien réserver : la base selon la surface, chaque option, le supplément d'urgence si la date est sous 48 h, la remise de parrainage du compte si elle existe, et montant, ce qui sera facturé. Sans prestations, les options habituelles du logement ; sans date, pas de supplément d'urgence. Avec les mêmes paramètres, POST /api/v1/menages donnera le même montant.
{
"donnees": {
"logement": { "id": 42, "nom": "Studio du lac" }, "date": "2026-10-20",
"surface": 45, "surDevis": false, "prestations": ["menage_courant", "vitres", "linge"],
"base": 65.0,
"options": [
{ "cle": "vitres", "libelle": "Vitres, baies vitrees et miroirs", "prix": 10.0 },
{ "cle": "linge", "libelle": "Kit linge & lits faits - 1 lit double, 1 lit simple", "prix": 16.0 }
],
"urgence": 0.0, "total": 91.0, "remiseParrainage": 0.0, "montant": 91.0, "dureeHeures": 2.0
}
}
Ménages
Réserve un ménage. Même parcours que dans l'application : grille tarifaire, remise de parrainage le cas échéant, paiement par facture mensuelle ou prélèvement sur la carte enregistrée, puis la mission est proposée aux Grooms.
| Champ | Type | Détail |
|---|---|---|
logementId | entier, requis | Un logement du compte. |
date | AAAA-MM-JJ, requis | Le jour du ménage, aujourd'hui ou plus tard. Sous 48 h, le supplément « urgence » de la grille s'applique. |
heure | HH:MM | Heure de passage souhaitée, 06:00 à 20:00. Par défaut 11:00. |
prestations | liste | vitres, linge, repassage, exterieur, grand_menage. Le ménage courant est toujours compris. Absent : les prestations du logement. Prix et libellés : GET /api/v1/prestations ; devis avant de réserver : GET /api/v1/logements/{id}/tarif. En lecture, un ménage peut aussi porter urgence : le supplément ajouté automatiquement sous 48 h. |
reference | texte ≤ 120 | Votre identifiant de réservation : rend l'appel idempotent et se retrouve dans chaque événement. |
curl -X POST https://groomly-app.com/api/v1/menages \
-H "Authorization: Bearer gk_live_…" -H "Content-Type: application/json" \
-d '{ "logementId": 42, "date": "2026-10-14", "heure": "11:00",
"prestations": ["linge"], "reference": "HMABCDEFGH" }'
201 Created
{ "donnees": { "id": 1087, "reference": "HMABCDEFGH", "statut": "EN_ATTENTE", "statutPaiement": "MENSUEL",
"montant": 81.0, "logement": { "id": 42, "nom": "Studio du lac" }, "date": "2026-10-14", "heure": "11:00", … } }
Les ménages du compte, du plus récent au plus ancien. Filtres : depuis et jusqua (AAAA-MM-JJ, sur la date du ménage), statut (EN_ATTENTE, CONFIRME, EN_COURS, TERMINE, ANNULE), logementId, limite (100 par défaut, 200 au plus).
Un ménage avec son Groom, ses photos et leurs preuves (voir Objets).
Annule un ménage, aux mêmes conditions que dans l'application : impossible une fois commencé, et impossible en ligne à moins de 24 h d'un ménage confirmé par un Groom (409 DELAI_DEPASSE). Un ménage déjà payé est remboursé.
Webhooks
Abonne une adresse https. Corps : { "url", "evenements": [ … ] } ; sans evenements, tous sont envoyés. La réponse contient le secret de signature, une seule fois ; Groomly le conserve chiffré. Cinq webhooks par compte au plus ; ils se gèrent aussi depuis l'onglet Développeur de votre espace.
Recevoir les événements
Groomly envoie une requête POST JSON à votre adresse à chaque étape d'un ménage. Répondez 2xx en moins de 10 secondes ; traitez le contenu ensuite, hors de la requête.
| Événement | Quand |
|---|---|
menage.reserve | Le ménage est payé (ou mis sur la facture mensuelle) et proposé aux Grooms. |
menage.confirme | Un Groom l'a accepté : le ménage porte son nom. |
menage.commence | Le Groom est sur place et a commencé. |
menage.termine | Le ménage est fait : les photos et vidéos, avec heure serveur et empreinte SHA-256, sont dans l'objet. |
menage.annule | Annulé par vous, par le calendrier, ou faute de Groom à temps. |
test.ping | Envoyé depuis le bouton « Tester » de l'espace, avec un ménage d'exemple. |
POST https://votre-logiciel.fr/webhooks/groomly
Content-Type: application/json; charset=utf-8
Groomly-Event: menage.termine
Groomly-Delivery: evt_8f0c…
Groomly-Signature: t=1759672800,v1=5f1e…
{ "id": "evt_8f0c…", "type": "menage.termine", "date": "2026-10-14T13:02:11+02:00",
"donnees": { "menage": { "id": 1087, "reference": "HMABCDEFGH", "statut": "TERMINE",
"photos": [ { "url": "…", "type": "PHOTO", "dateServeur": "2026-10-14T12:58:40+02:00",
"sha256": "3f9a2c…", "tache": "Cuisine" } ], … } } }
Vérifier la signature
L'en-tête Groomly-Signature contient l'horodatage t (secondes Unix) et v1, le HMAC-SHA256 en hexadécimal, calculé avec votre secret sur la chaîne t + "." + corps, où corps est le texte brut reçu, octet pour octet. Comparez en temps constant, et rejetez tout événement dont t a plus de cinq minutes : c'est ce qui empêche de rejouer un ancien message.
// Node.js
const crypto = require('crypto');
function verifier(secret, entete, corpsBrut) {
const p = Object.fromEntries(entete.split(',').map(x => x.split('=')));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const attendu = crypto.createHmac('sha256', secret).update(p.t + '.' + corpsBrut).digest('hex');
return attendu.length === p.v1.length && crypto.timingSafeEqual(Buffer.from(attendu), Buffer.from(p.v1));
}
# Python
import hmac, hashlib, time
def verifier(secret, entete, corps_brut):
p = dict(x.split('=') for x in entete.split(','))
if abs(time.time() - int(p['t'])) > 300: return False
attendu = hmac.new(secret.encode(), f"{p['t']}.{corps_brut}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(attendu, p['v1'])
Groomly-Delivery (le même que id dans le corps). Gardez-le : une reprise renvoie le même identifiant, votre traitement peut ainsi rester idempotent.Reprises
Sans réponse 2xx, Groomly réessaie après 1 minute, 10 minutes, 1 heure, 6 heures puis 24 heures, et abandonne ensuite. Après 30 échecs consécutifs, le webhook est désactivé ; il se réactive d'un clic dans l'onglet Développeur, où chaque livraison et sa réponse sont visibles. Les événements ne sont jamais envoyés dans un ordre garanti : fiez-vous au statut de l'objet, pas à l'ordre d'arrivée.
Objets
Ménage
| Champ | Détail |
|---|---|
id, reference | Identifiant Groomly ; votre référence, si vous l'avez donnée. |
logement | { id, nom } |
date, heure, creneau | Jour (AAAA-MM-JJ), heure de passage, matin ou aprem. |
statut | EN_ATTENTE (proposé aux Grooms) → CONFIRME → EN_COURS → TERMINE, ou ANNULE. |
statutPaiement | MENSUEL (facture mensuelle), EN_COURS, PAYE, REFUSE, REMBOURSE, ANNULE. |
prestations, duree, montant | Prestations, durée estimée en heures, montant TTC en euros. |
groom | { nom, photoUrl } dès qu'un Groom a accepté, sinon null. |
photos | Liste de { url, type (PHOTO|VIDEO), dateServeur, sha256, tache }. Prises dans l'application, sur place ; dateServeur est l'heure de réception sur nos serveurs et sha256 l'empreinte du fichier reçu, vérifiable par GET /api/preuve/verifier?sha256=…. |
avis | { note, commentaire } laissé par le propriétaire, sinon null. |
origine | API, CALENDRIER (créé par le calendrier iCal du logement) ou MANUEL. |
Logement
id, nom, type, adresse { rue, codePostal, ville }, surface, prestations, litsDouble, litsSimple, tarifEstime, calendrier { actif, etat }, dateCreation.
Support
Une question, un cas qui n'entre pas dans ces pages, une intégration à construire ensemble : écrivez à contact@groomly-app.com en précisant le préfixe de votre clé (gk_live_ab12cd34, jamais la clé complète). L'API est versionnée dans son chemin : /api/v1 restera stable, et tout changement incompatible vivra dans une /api/v2.