Démarrage rapide
- Connectez-vous à EloCRM, ouvrez Paramètres → Clés API (plan Business requis).
- Créez une clé : nommez-la (ex. Production · Zapier) et sélectionnez les permissions nécessaires.
- Copiez la clé immédiatement — elle commence par
thia_sk_et ne sera plus jamais affichée. - Effectuez votre premier appel — par défaut sur
/api/rpc/<procedure>.
curl -X POST https://app.elocrm.io/api/rpc/clients/list \
-H "Authorization: Bearer thia_sk_..." \
-H "Content-Type: application/json" \
-d '{}'Authentification
Toutes les requêtes API doivent porter un header Authorization: Bearer <clé>. Le serveur résout l'organisation associée et applique automatiquement l'isolation multi-tenant — aucune donnée d'autres agences n'est jamais accessible.
- Format :
thia_sk_+ 32 caractères aléatoires. - Stocké en SHA-256 côté serveur — la clé en clair est perdue dès qu'elle quitte la dialog de création.
- Permissions au format
module:action(par ex.clients:read,invoices:write). Le scope*donne accès à tout. - Une clé peut être révoquée à tout instant ; les intégrations cessent immédiatement.
Catalogue de permissions
Chaque clé API porte un ensemble de permissions au format module:action. Le scope * donne accès à tout — réservez-le aux scripts internes critiques. Pour les intégrations tierces, suivez le principe du moindre privilège.
| Scope | Description |
|---|---|
| * | Tout (lecture + écriture) |
| clients:read | Lire les clients et notes |
| clients:write | Créer / modifier / supprimer des clients |
| projects:read | Lire les projets |
| projects:write | Créer / modifier des projets |
| tasks:read | Lire les tâches |
| tasks:write | Créer / modifier / clôturer des tâches |
| quotes:read | Lire les devis |
| quotes:write | Créer / envoyer des devis |
| invoices:read | Lire les factures |
| invoices:write | Créer / envoyer / payer des factures |
| appointments:read | Lire l'agenda |
| appointments:write | Créer / modifier des RDV |
| time:read | Lire les saisies de temps |
| time:write | Démarrer / modifier des timers |
Endpoints
L'API EloCRM est exposée via oRPC sur POST /api/rpc/<procedure>. La référence OpenAPI complète est générée automatiquement et navigable dans Scalar — vous pouvez y exécuter des requêtes directement en collant votre clé.
Familles d'endpoints : clients, projects, tasks, quotes, invoices, appointments, time, analytics, webhooks, apiKeys.
Codes d'erreur
Chaque erreur renvoie un statut HTTP standard ainsi qu'un objet { code, message } dans le body. Inspectez le message pour des détails utilisateur.
| HTTP | Code | Cause | Solution |
|---|---|---|---|
| 401 | UNAUTHORIZED | Clé invalide, révoquée, expirée, ou organisation désactivée. | Vérifier la clé dans Paramètres → Clés API. |
| 403 | FORBIDDEN | Permission insuffisante OU plan trop bas pour la fonctionnalité. | Ajouter la permission OU upgrader le plan. |
| 404 | NOT_FOUND | Ressource introuvable ou hors de votre organisation. | Vérifier l'ID. |
| 400 | BAD_REQUEST | Validation Zod échouée — champ manquant ou type incorrect. | Lire le champ message de la réponse. |
| 409 | CONFLICT | Doublon (email, numéro de facture déjà pris). | Récupérer l'enregistrement existant. |
Webhooks sortants
Configurez vos endpoints depuis Paramètres → Webhooks (plan Pro requis). EloCRM envoie un POST signé HMAC-SHA256 à chaque événement souscrit. 3 tentatives avec backoff (1 s, 10 s, 60 s) ; timeout réseau 10 s par tentative.
- Header
x-webhook-signature: HMAC-SHA256 du body brut en hexadécimal. - Header
x-webhook-event: type d'événement (routage rapide). - Réponse
2xx= succès, stoppe la boucle. Tout le reste déclenche un retry. - Idempotence recommandée côté receveur (un même événement peut arriver deux fois en cas de timeout).
Forme du payload
{
"event": "invoice.paid",
"timestamp": "2026-04-26T10:32:11.412Z",
"data": {
"type": "invoice.paid",
"orgId": "org_acme_xyz",
"invoiceId": "ckxyz...",
"clientId": "ckabc...",
"actorId": "usr_..."
}
}Événements disponibles
13 événements sont exposés aux webhooks. Les événements internes (mentions de notes, alertes) restent privés à l'application.
| Événement | Description |
|---|---|
| client.created | Client créé |
| client.updated | Client mis à jour |
| project.created | Projet créé |
| task.created | Tâche créée |
| task.completed | Tâche terminée |
| task.reopened | Tâche réouverte |
| appointment.created | RDV créé |
| quote.sent | Devis envoyé |
| quote.signed | Devis signé |
| invoice.created | Facture créée |
| invoice.sent | Facture envoyée |
| invoice.paid | Facture payée |
| document.shared | Document partagé |
Vérifier la signature HMAC
La signature prouve que le webhook vient bien d'EloCRM. Calculez HMAC-SHA256 sur le body brut avec votre secret et comparez en temps constant (jamais avec ===).
Node.js (Express)
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
function verifyEloSignature(rawBody, signature, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
}
app.post(
"/webhooks/elocrm",
express.raw({ type: "application/json" }),
(req, res) => {
const ok = verifyEloSignature(
req.body,
req.headers["x-webhook-signature"],
process.env.ELO_WEBHOOK_SECRET,
);
if (!ok) return res.status(401).send("Invalid signature");
const payload = JSON.parse(req.body.toString());
// ... traiter en async, répondre tout de suite
res.status(200).end();
},
);Python (Flask)
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
def verify_elo_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
@app.post("/webhooks/elocrm")
def receive_elo():
if not verify_elo_signature(
request.get_data(),
request.headers.get("X-Webhook-Signature", ""),
os.environ["ELO_WEBHOOK_SECRET"],
):
abort(401)
# ... traiter en async, répondre tout de suite
return "", 200PHP
<?php
$rawBody = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_WEBHOOK_SIGNATURE"] ?? "";
$secret = getenv("ELO_WEBHOOK_SECRET");
$expected = hash_hmac("sha256", $rawBody, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit("Invalid signature");
}
http_response_code(200);
// ... traiter en async (queue, job worker, etc.)Exemples — appels API
curl — lister les clients
curl -X POST https://crm.thiagency.be/api/rpc/clients/list \
-H "Authorization: Bearer thia_sk_abc123..." \
-H "Content-Type: application/json" \
-d '{}'curl — créer un client
curl -X POST https://crm.thiagency.be/api/rpc/clients/create \
-H "Authorization: Bearer thia_sk_abc123..." \
-H "Content-Type: application/json" \
-d '{
"firstName": "Jean",
"lastName": "Dupont",
"email": "jean@dupont.be",
"companyName": "Dupont SA"
}'Node.js (fetch) — gestion des erreurs
const ELO_API = "https://crm.thiagency.be/api/rpc";
const ELO_KEY = process.env.ELO_API_KEY; // jamais hardcodé
async function listClients() {
const res = await fetch(`${ELO_API}/clients/list`, {
method: "POST",
headers: {
Authorization: `Bearer ${ELO_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
if (!res.ok) {
throw new Error(`ELO API ${res.status}: ${await res.text()}`);
}
const { json } = await res.json();
return json;
}
const clients = await listClients();
console.log(`${clients.length} clients chez THIA'gency`);Python (requests) — lister les factures
import os, requests
ELO_API = "https://crm.thiagency.be/api/rpc"
ELO_KEY = os.environ["ELO_API_KEY"]
def list_invoices():
r = requests.post(
f"{ELO_API}/invoices/list",
headers={
"Authorization": f"Bearer {ELO_KEY}",
"Content-Type": "application/json",
},
json={},
timeout=10,
)
r.raise_for_status()
return r.json()
invoices = list_invoices()
print(f"{len(invoices)} factures")Rate limits
L'API EloCRM applique une politique souple : pour l'instant, aucun quota strict n'est en place — un système de rate-limit par clé est prévu pour la prochaine itération. Les abus manifestes (bursts répétés au-dessus de 200 req/sec) peuvent entraîner une révocation manuelle.
Pour les intégrations à fort volume, contactez-nous sur dev@elocrm.io — nous pouvons fournir des clés dédiées avec quotas relevés.