Rendez-vous depuis votre propre planning
Placez les rendez-vous de votre propre logiciel de planning dans l'agenda de Klantly, gardez-les à jour et laissez Klantly envoyer une confirmation au client.
Vous planifiez dans votre propre système, par exemple un tableau de planning pour vos techniciens ? Avec l'API, vous placez ces rendez-vous dans l'agenda de Klantly. Vous les retrouvez alors sur la fiche du client et sur votre tableau de pipeline, et votre client reçoit les mêmes confirmations et rappels que pour un rendez-vous créé dans Klantly même.
Ce dont vous avez besoin
Une clé API avec ces scopes :
| Scope | Pour quoi faire |
|---|---|
customers.read |
Rechercher le client par adresse e-mail. |
customers.write |
Créer un client qui n'existe pas encore. |
appointments.read |
Lister les rendez-vous et les créneaux libres. |
appointments.write |
Planifier, déplacer, annuler et terminer des rendez-vous. |
appointments.send |
Envoyer au client une confirmation, un déplacement, une annulation ou un rappel par e-mail. |
La fonctionnalité Rendez-vous doit être activée pour votre entreprise.
Étape 1 : le client
Un rendez-vous appartient toujours à un client ; le nom, l'e-mail et le téléphone viennent de lui. Recherchez le client avec filter[email] ou créez-le, comme dans Du formulaire web au prospect. Conservez son id dans votre propre système : vous n'aurez pas à le rechercher la prochaine fois.
Étape 2 : planifier le rendez-vous
curl -X POST "https://app.klantly.com/api/v1/appointments" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: planning-4711" \
-d '{"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70", "title": "Métré véranda", "starts_at": "2026-10-06T09:00:00+02:00", "ends_at": "2026-10-06T10:30:00+02:00", "location": "12 rue du Village, Lille"}'- Envoyez toujours les heures avec un fuseau horaire, par exemple
+02:00ouZ. La réponse les donne en UTC. - Sans
ends_at, le rendez-vous dure autant que le type de rendez-vous indiqué dansappointment_type_id, sinon la durée par défaut de vos paramètres de rendez-vous. - Mettez l'id de votre propre planning dans l'
Idempotency-Key. Si votre serveur réessaie après un délai d'attente, il n'y a toujours qu'un seul rendez-vous. - Conservez l'
idde la réponse à côté de votre propre id. Vous en avez besoin pour modifier le rendez-vous plus tard.
Un nouveau rendez-vous est confirmed. S'il doit encore être confirmé, envoyez "status": "pending". Klantly ne vérifie pas la disponibilité lors de la planification : votre planning fait foi.
Remarque
L'API n'envoie pas d'e-mail au client lors de la planification. Vous le faites délibérément, à l'étape 4. Attention : si votre entreprise a des automatisations sur « rendez-vous planifié » (par exemple une confirmation WhatsApp), elles s'exécutent, comme pour un rendez-vous dans l'agenda. Désactivez-les un moment lorsque vous importez votre planning pour la première fois.
Étape 3 : déplacer, annuler et terminer
Si l'heure change dans votre planning, envoyez uniquement la nouvelle heure :
curl -X PATCH "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"starts_at": "2026-10-08T13:00:00+02:00"}'Sans ends_at, la durée reste identique, et rescheduled_at indique que le rendez-vous a été déplacé. Si un rendez-vous n'a pas lieu, annulez-le au lieu de le supprimer : il reste ainsi visible sur la fiche du client.
curl -X POST "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09/cancel" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "Le client est malade"}'Une fois la visite effectuée, terminez le rendez-vous avec POST /appointments/{id}/complete. Si votre conversion des prospects est réglée sur « rendez-vous terminé », un prospect devient client, comme dans l'agenda.
Étape 4 : informer le client
curl -X POST "https://app.klantly.com/api/v1/appointments/0b6f3a52-7c1d-4e8a-9b2f-5d4c3b2a1f09/notify" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: planning-4711-confirmation" \
-d '{"message": "confirmation"}'Klantly envoie l'e-mail avec le modèle que vous avez configuré dans Klantly. Le message doit correspondre au statut du rendez-vous :
message |
Possible avec le statut |
|---|---|
confirmation |
confirmed |
reschedule |
pending ou confirmed |
cancellation |
cancelled |
reminder |
confirmed |
Si ce n'est pas le cas, si le rendez-vous n'a pas de date, si le client n'a pas d'adresse e-mail ou si votre entreprise a désactivé le modèle, vous recevez une 409 avec le code invalid_state_transition. detail en donne la raison.
Dans l'autre sens : les réservations depuis Klantly
Les clients peuvent aussi réserver eux-mêmes via votre page de réservation. Pour les voir dans votre planning, écoutez avec un webhook les événements appointment.created, appointment.updated, appointment.confirmed, appointment.cancelled, appointment.completed et appointment.deleted. Un changement de statut arrive comme événement distinct, pas comme appointment.updated. Chaque événement contient le rendez-vous complet.
Ces événements arrivent aussi pour les rendez-vous que vous avez planifiés vous-même via l'API. Reconnaissez-les à l'id conservé à l'étape 2, sinon ils apparaîtront deux fois dans votre planning.
Consulter les créneaux libres
Pour voir dans votre propre système où Klantly a encore de la place, demandez les créneaux libres :
curl --globoff "https://app.klantly.com/api/v1/availability?date_from=2026-10-06&date_to=2026-10-10&duration_minutes=90" \
-H "Authorization: Bearer $KLANTLY_API_KEY"Ce sont presque les mêmes créneaux que sur votre page de réservation : horaires, pauses, jours bloqués, délai de réservation maximal et rendez-vous déjà réservés sont pris en compte. Seul votre agenda Google n'est pas pris en compte ici, et les horaires valent pour toute l'entreprise, pas par collaborateur. Vous demandez au maximum 31 jours à la fois.
Tout ensemble
use GuzzleHttp\Client;
/**
* $klantly est un client Guzzle avec l'URL de base et votre clé API, $job un rendez-vous de votre propre
* planning. Renvoie l'id du rendez-vous dans Klantly : conservez-le avec le job.
*/
function syncAppointment(Client $klantly, array $job): string
{
$body = [
'title' => $job['title'],
'starts_at' => $job['start']->format(DATE_ATOM),
'ends_at' => $job['end']->format(DATE_ATOM),
'location' => $job['address'],
];
// Nouveau : planifier et envoyer une confirmation au client.
if ($job['klantly_id'] === null) {
$appointment = json_decode((string) $klantly->post('appointments', [
'headers' => ['Idempotency-Key' => "planning-{$job['id']}"],
'json' => $body + ['customer_id' => $job['klantly_customer_id']],
])->getBody(), true)['data'];
$klantly->post("appointments/{$appointment['id']}/notify", [
'headers' => ['Idempotency-Key' => "planning-{$job['id']}-confirmation"],
'json' => ['message' => 'confirmation'],
]);
return $appointment['id'];
}
// Existant : le modifier et, si l'heure change, prévenir le client du déplacement.
$klantly->patch("appointments/{$job['klantly_id']}", ['json' => $body]);
if ($job['time_changed']) {
$klantly->post("appointments/{$job['klantly_id']}/notify", [
'json' => ['message' => 'reschedule'],
]);
}
return $job['klantly_id'];
}const BASE_URL = 'https://app.klantly.com/api/v1';
async function klantly(method, path, { body, idempotencyKey } = {}) {
const response = await fetch(`${BASE_URL}/${path}`, {
method,
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
...(body ? { 'Content-Type': 'application/json' } : {}),
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const json = await response.json();
if (!response.ok) throw new Error(json.detail ?? json.title);
return json;
}
// job est un rendez-vous de votre propre planning ; start et end sont en ISO 8601 avec fuseau horaire.
// Renvoie l'id du rendez-vous dans Klantly : conservez-le avec le job.
export async function syncAppointment(job) {
const body = { title: job.title, starts_at: job.start, ends_at: job.end, location: job.address };
// Nouveau : planifier et envoyer une confirmation au client.
if (!job.klantlyId) {
const { data } = await klantly('POST', 'appointments', {
body: { ...body, customer_id: job.klantlyCustomerId },
idempotencyKey: `planning-${job.id}`,
});
await klantly('POST', `appointments/${data.id}/notify`, {
body: { message: 'confirmation' },
idempotencyKey: `planning-${job.id}-confirmation`,
});
return data.id;
}
// Existant : le modifier et, si l'heure change, prévenir le client du déplacement.
await klantly('PATCH', `appointments/${job.klantlyId}`, { body });
if (job.timeChanged) {
await klantly('POST', `appointments/${job.klantlyId}/notify`, { body: { message: 'reschedule' } });
}
return job.klantlyId;
}Gérer les erreurs
422avecvalidation_failed: par exemple une heure sans fuseau horaire.errorsindique par champ ce qui ne va pas.409avecinvalid_state_transition: par exemple terminer un rendez-vous annulé, ou un message qui ne correspond pas au statut.429avecrate_limited: attendez le nombre de secondes indiqué dansRetry-Afteret réessayez. Lorsque vous importez votre planning pour la première fois, étalez les requêtes dans le temps. Voir Rate limits.
Dernière mise à jour le 15 septembre 2026