Du formulaire web au prospect
Enregistrez chaque demande envoyée via le formulaire de votre site comme prospect dans Klantly, avec le message en note et, si vous le souhaitez, un deal.
Un visiteur remplit le formulaire de contact de votre site. En quelques requêtes, cette demande devient un prospect dans Klantly, avec le message joint et une carte sur votre tableau de pipeline.
Attention
Envoyez le formulaire à votre propre serveur et faites les requêtes API depuis celui-ci. Une clé API n'a jamais sa place dans le code de votre site : n'importe qui peut l'y lire.
Ce dont vous avez besoin
Une clé API avec ces scopes :
| Scope | Pour quoi faire |
|---|---|
customers.read |
Vérifier si l'adresse e-mail est déjà connue. |
customers.write |
Créer le prospect et ajouter la note. |
deals.write |
Facultatif : placer le prospect sur le tableau de pipeline. |
Étape 1 : le client existe-t-il déjà ?
Une adresse e-mail est unique au sein de votre entreprise. Vérifiez donc d'abord si elle est déjà connue :
curl --globoff "https://app.klantly.com/api/v1/customers?filter[email]=jan@example.com" \
-H "Authorization: Bearer $KLANTLY_API_KEY"Si data est vide, créez le prospect. Sinon, utilisez l'id du client renvoyé.
Étape 2 : créer le prospect
curl -X POST "https://app.klantly.com/api/v1/customers" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-8f14e45f" \
-d '{"email": "jan@example.com", "name": "Jan de Vries", "phone": "+31 6 12345678"}'Donnez à chaque envoi sa propre Idempotency-Key, par exemple l'id de l'envoi dans votre propre base de données. Si un visiteur clique deux fois sur envoyer, ou si votre serveur réessaie après un délai dépassé, un seul prospect est créé. Pour en savoir plus, voir Idempotence.
Si deux envois avec la même adresse e-mail arrivent presque en même temps, le second peut renvoyer une 422 avec une erreur sur email. Recherchez alors à nouveau le client, comme à l'étape 1.
Étape 3 : le message en note
curl -X POST "https://app.klantly.com/api/v1/customers/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/notes" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-8f14e45f-note" \
-d '{"content": "Via le formulaire du site : je souhaite un devis pour une véranda de 5 sur 3 mètres."}'Étape 4 : un deal sur le tableau (facultatif)
curl -X POST "https://app.klantly.com/api/v1/deals" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-8f14e45f-deal" \
-d '{"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70", "title": "Véranda 5x3 m"}'Un client n'a qu'un seul deal ouvert à la fois. S'il en a déjà un, vous recevez 409 avec le code conflict et l'id de ce deal dans deal_id. Ce n'est pas une vraie erreur : la demande se rattache alors au deal déjà en cours.
Tout ensemble
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
function sendLeadToKlantly(array $form, string $submissionId): void
{
$klantly = new Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
// 1. Le client existe-t-il déjà ?
$found = json_decode((string) $klantly->get('customers', [
'query' => ['filter' => ['email' => $form['email']]],
])->getBody(), true)['data'];
// 2. Sinon : créer le prospect.
$customer = $found[0] ?? json_decode((string) $klantly->post('customers', [
'headers' => ['Idempotency-Key' => "form-{$submissionId}"],
'json' => array_filter(['email' => $form['email'], 'name' => $form['name'], 'phone' => $form['phone']]),
])->getBody(), true)['data'];
// 3. Le message en note.
$klantly->post("customers/{$customer['id']}/notes", [
'headers' => ['Idempotency-Key' => "form-{$submissionId}-note"],
'json' => ['content' => $form['message']],
]);
// 4. Un deal ; 409 signifie qu'un deal ouvert existe déjà.
try {
$klantly->post('deals', [
'headers' => ['Idempotency-Key' => "form-{$submissionId}-deal"],
'json' => ['customer_id' => $customer['id'], 'title' => $form['subject']],
]);
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() !== 409) {
throw $e;
}
}
}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,
});
return { status: response.status, json: await response.json() };
}
export async function sendLeadToKlantly(form, submissionId) {
// 1. Le client existe-t-il déjà ?
const found = await klantly('GET', `customers?${new URLSearchParams({ 'filter[email]': form.email })}`);
if (found.status !== 200) throw new Error(found.json.detail ?? found.json.title);
let customer = found.json.data[0];
// 2. Sinon : créer le prospect.
if (!customer) {
const created = await klantly('POST', 'customers', {
body: { email: form.email, name: form.name, ...(form.phone ? { phone: form.phone } : {}) },
idempotencyKey: `form-${submissionId}`,
});
if (created.status !== 201) throw new Error(created.json.detail ?? created.json.title);
customer = created.json.data;
}
// 3. Le message en note.
await klantly('POST', `customers/${customer.id}/notes`, {
body: { content: form.message },
idempotencyKey: `form-${submissionId}-note`,
});
// 4. Un deal ; 409 signifie qu'un deal ouvert existe déjà.
const deal = await klantly('POST', 'deals', {
body: { customer_id: customer.id, title: form.subject },
idempotencyKey: `form-${submissionId}-deal`,
});
if (deal.status !== 201 && deal.status !== 409) throw new Error(deal.json.detail ?? deal.json.title);
}Gérer les erreurs
422avecvalidation_failed: par exemple une adresse e-mail invalide.errorsindique pour chaque champ ce qui ne va pas. Laissez le visiteur corriger.429avecrate_limited: attendez le nombre de secondes indiqué dansRetry-Afteret réessayez. Voir Limites de débit.- Une erreur
5xxou un délai dépassé : réessayez plus tard avec la mêmeIdempotency-Key. Placez de préférence l'envoi dans une file d'attente : ainsi, aucune demande ne se perd.
Astuce
Vous voulez savoir quand un prospect devient client ? Écoutez l'événement customer.converted avec un webhook.
Dernière mise à jour le 15 septembre 2026