FixVault CRM — API Reference
Doc complète pour intégrer un outil externe (diagnostic Android/iOS, app desktop, etc.) au CRM FixVault.
🔒 Cette page est publique. La clé de service de production n'est PAS publiée ici — demandez-la à
e.repare33@gmail.compar canal privé. Voir section 1.A.
Base URL : https://app.fixvault.fr
Versioning : pas de préfixe de version (contrat stable par compatibilité ascendante).
Format : JSON (request + response).
Fuseau horaire : Europe/Paris. Dates en ISO 8601 UTC.
1. Authentification
2 modes disponibles.
A. Service API Key (backend-to-backend, lecture seule)
Pour les intégrations serveur (ex : cron, outil distant côté serveur).
- Clé générée dans Paramètres → Accès API (
tenant.settings.serviceApiKey). - Envoyée dans le header
x-service-key. - Donne accès uniquement aux endpoints sous
/api/service/*. - Lecture seule : clients, tickets, factures, devis. Pas de création/modification.
Génération de la clé : chaque tenant génère sa propre clé via Paramètres → Accès API → Service API Key. Une clé par boutique.
⚠️ Cette clé donne accès en lecture aux données du CRM (clients, tickets, factures). Ne JAMAIS l'embarquer dans un binaire distribué publiquement, ni la commit dans un repo public. À conserver dans un fichier
.envnon versionné côté outil. Demander la clé à l'admin de la boutique par canal privé (Signal, mail crypté, gestionnaire de secrets).
GET /api/service/tickets?keyword=iphone
x-service-key: <CLE_DE_LA_BOUTIQUE>
B. JWT access token (utilisateur connecté — full API)
Pour une app qui doit créer / modifier des données, agissant au nom d'un employé.
Flow :
POST /api/auth/loginavec{ email, password }→ retourne{ accessToken, refreshToken, user }.- Mettre
Authorization: Bearer <accessToken>sur chaque requête. - Si 401, faire
POST /api/auth/refreshavec{ refreshToken }→ nouveau access token. - Access token valide 15 min ; refresh token valide 7 jours.
POST /api/auth/login
Content-Type: application/json
{
"email": "admin@boutique.fr",
"password": "MotDePasse123"
}
{
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc...",
"user": { "id": "...", "email": "...", "role": "ADMIN", "tenantId": "..." }
}
Si 2FA activé sur le compte, la réponse est { requires2FA: true, tempToken }. Appeler ensuite POST /api/auth/2fa/validate avec { tempToken, code }.
2. Endpoints /api/service/* (service key)
Tickets
GET /api/service/tickets?email=jdupont@example.com&page=1&pagesize=50
GET /api/service/tickets?customer_id=<UUID>
GET /api/service/tickets?from_date=<unix>&to_date=<unix>
Params :
keyword/email: recherche dans email, mobile, code, prénom, nom du clientcustomer_id: UUID client OU ancienexternalIdRepairDeskfrom_date,to_date: timestamps unix en secondespage(≥1, défaut 1),pagesize(1–500, défaut 50)
Response format RepairDesk-compatible :
{
"success": true,
"total": 42,
"data": [
{
"id": "uuid",
"ticket_number": "T-7456",
"order_id": "T-7456",
"customer_id": "42",
"customer_name": "Jean Dupont",
"customer_email": "j.dupont@example.com",
"customer_mobile": "+33611223344",
"device_name": "iPhone 13 128 Go Noir",
"device_brand": "Apple",
"device_model": "iPhone 13",
"device_category": "Téléphones",
"device_fault": "Écran cassé",
"device_image_url": "https://.../iphone-13.jpg",
"brand_image_url": "https://.../apple-logo.png",
"status": { "id": "IN_REPAIR", "name": "En cours de réparation" },
"created_at": "2026-04-15T12:34:56.000Z",
"updated_at": "2026-04-16T09:15:00.000Z",
"total_amount": 120,
"images": ["https://app.fixvault.fr/uploads/abc.jpg"],
"repair_items": ["Remplacement écran Soft OLED"],
"devices": [
{
"id": "asset-uuid",
"assetCode": "A-0042",
"device_name": "iPhone 13 128 Go Noir",
"device_brand": "Apple",
"device_model": "iPhone 13",
"device_image_url": "...",
"imei": "358123456789012",
"serial": "C8PR2GH3XYZ",
"isPrimary": true,
"repairItems": ["Remplacement écran Soft OLED"]
}
]
}
]
}
Détail ticket
GET /api/service/tickets/:id
:id peut être UUID ou ticketCode (T-7456 ou 7456).
Factures
GET /api/service/invoices?keyword=<email>&customer_id=<id>
GET /api/service/invoices/:id/pdf # PDF binaire
Devis
GET /api/service/estimates/:id/pdf
Clients
GET /api/service/customers?keyword=dupont&page=1&pagesize=25
GET /api/service/customers/:id # détail
GET /api/service/customer?email=... # profil complet + tickets + factures + devis + fidélité
Le /api/service/customer agrège :
- Infos client
- Derniers tickets, factures, devis
- Solde points fidélité
- Historique dépenses
3. Endpoints authentifiés (JWT) pour intégration d'outils
Utile pour l'outil de diagnostic qui doit modifier des données.
Créer un ticket
POST /api/tickets
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"customerId": "uuid", // optionnel, sinon walk-in
"deviceName": "iPhone 13 Pro",
"manufacturer": "Apple",
"category": "Téléphones",
"task": "Diagnostique",
"diagnosticNotes": "Ne s'allume plus, voyant vert",
"imei": "358123456789012",
"serialNumber": "C8PR2GH3XYZ",
"securityCode": "1234",
"isRushJob": false,
"status": "INTAKE",
"warrantyMonths": 3,
"dueDate": "2026-04-20",
"images": ["https://app.fixvault.fr/uploads/abc.jpg"]
}
Retour : le ticket créé avec son ticketCode (T-XXXX) et id UUID.
Mettre à jour le ticket (diagnostic, notes, statut)
PATCH /api/tickets/:id/status
{ "status": "DIAGNOSIS" }
Statuts disponibles : INTAKE, DIAGNOSIS, WAITING_QUOTE, WAITING_PARTS, WAITING_REPAIR, IN_REPAIR, BACKUP_DONE, READY, DELIVERED, UNREPAIRABLE, WAITING_DROPOFF, CANCELLED, CLOSED.
Une notification SMS+email est automatiquement envoyée au client si configuré.
PATCH /api/tickets/:id
{
"diagnosticNotes": "Batterie HS, remplacement nécessaire",
"task": "Remplacement batterie"
}
Ajouter un commentaire / log
POST /api/tickets/:id/comments
{
"type": "DIAGNOSTIC", // COMMENT | DIAGNOSTIC | SYSTEM | SMS | EMAIL
"content": "Test GSX : device blacklisté US",
"isPrivate": false // true = interne, pas visible client
}
Ajouter une ligne (service/pièce)
POST /api/tickets/:id/lines
{
"lineType": "SERVICE", // SERVICE | PART | PRODUCT
"description": "Remplacement batterie",
"repairServiceId": "uuid", // optionnel, si service catalogue existe
"unitPrice": 49.90, // TTC — la TVA est extraite auto si service.taxInclusive
"quantity": 1,
"warrantyDays": 180,
"customerAssetId": "asset-uuid" // optionnel, scope à l'appareil (multi-device)
}
Upload de fichier (photo diagnostic, rapport)
POST /api/upload
Authorization: Bearer <accessToken>
Content-Type: multipart/form-data
file: <binary>
Réponse : { url: "https://app.fixvault.fr/uploads/abc-xyz.jpg", filename: "...", size: 123456 }.
Utilisable ensuite dans ticket.images[] ou ticket.testReportUrl.
Catalogue appareils (marques + modèles)
GET /api/devices/brands?search=apple
GET /api/devices/models?brandId=<uuid>&category=Téléphones
GET /api/public/repair-catalog?tenant=<slug>&category=phone # public, sans auth
Clients
GET /api/customers?search=dupont
POST /api/customers
PATCH /api/customers/:id
POST /api/customers/:id/contacts # ajouter un email/mobile secondaire
Payload create customer :
{
"firstName": "Jean",
"lastName": "Dupont",
"email": "j.dupont@example.com",
"mobile": "+33611223344",
"address": "10 rue du test",
"postalCode": "75001",
"city": "Paris",
"notifyEmail": true,
"notifySms": true,
"source": "API"
}
Appareils du client (customer assets)
GET /api/customers/:id/assets
POST /api/assets
{
"customerId": "uuid",
"name": "iPhone 13 de Jean",
"deviceModelId": "uuid",
"imei": "358123456789012",
"serialNumber": "C8PR2GH3XYZ",
"pinPattern": "1234"
}
Un asset peut être attaché à plusieurs tickets via POST /api/tickets/:id/assets.
4. Rate limiting
- Global : 100 requêtes / min (par IP).
- Service API : 60 req / min, 1000 req / h, 10 000 req / jour.
- Endpoints auth : 5 req / 15min (login), 3 req / h (register), 10 req / min (refresh).
Un dépassement renvoie 429 Too Many Requests + header Retry-After en secondes.
5. Codes d'erreur
| Code | Signification |
|---|---|
| 200 / 201 | OK |
| 400 | Validation échouée (body details.fieldErrors) |
| 401 | Token absent / invalide / expiré |
| 403 | Permission refusée |
| 404 | Ressource introuvable |
| 409 | Conflit (doublon unique, statut incompatible) |
| 422 | Transition métier invalide (ex : fermer un ticket déjà livré) |
| 429 | Rate limit dépassé |
| 500 | Erreur serveur (à reporter) |
Body erreur standard :
{ "error": "Validation failed", "details": { "fieldErrors": { "email": ["Invalid email"] } } }
6. Workflow suggéré pour un outil de diagnostic Android/iOS
- Employé ouvre l'outil sur son PC pendant que le client dépose son appareil.
- Outil se connecte à FixVault en JWT (login employé + 2FA si activé).
- Recherche du client existant ou création à la volée :
GET /api/customers/check-duplicate?email=...&mobile=...POST /api/customers (si absent)
- Création du ticket en statut
INTAKEavecdeviceName + manufacturer + imei. - Détection appareil côté Android/iOS (via ADB / libimobiledevice) → récupération infos système (modèle, OS, batterie, réseau, etc.).
- Exécution de tests (écran, multi-touch, haut-parleurs, micro, caméras, capteurs, charge, GPS, Bluetooth, Wi-Fi, NFC, etc.).
- Pour chaque test :
POST /api/tickets/:id/commentstypeDIAGNOSTIC+ résultat. - Upload des captures/rapport :
POST /api/upload→ mettretestReportUrlsur le ticket. - Changement de statut final :
PATCH /api/tickets/:id/status→DIAGNOSIS, puisWAITING_QUOTEouREADYselon le diagnostic. - Le client reçoit automatiquement les notifications statut (SMS + email) configurées dans FixVault.
7. Module Diagnostic (Android/iOS/Windows)
API dédiée pour stocker des résultats de tests structurés par session et par appareil. Permet d'historiser les diagnostics d'un même ticket et de comparer avant/après réparation.
Modèle
- DiagnosticSession : 1 passage de tests sur 1 appareil
- DiagnosticTestResult : 1 ligne par test exécuté (status pass/fail/warn/skip + valeur)
Plusieurs sessions par ticket possibles (retests autorisés).
Créer une session
POST /api/diagnostic-sessions
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"ticketId": "uuid-du-ticket",
"deviceSide": "ios", // ios | android | windows | macos
"deviceModel": "iPhone15,2",
"osVersion": "17.4.1",
"serialNumber": "C8PR2GH3XYZ",
"imei": "358123456789012",
"runBy": "john@boutique.fr", // employé, optionnel
"rawData": { "anyKey": "anyValue" } // JSON libre (config matérielle, etc.)
}
Réponse : { id, ticketId, runAt, ... }. Si le ticket était INTAKE, il bascule automatiquement en DIAGNOSIS.
Pousser les résultats de tests (batch)
POST /api/diagnostic-sessions/:id/tests
{
"tests": [
{ "category": "display", "name": "Multi-touch", "status": "pass", "value": "10/10 points" },
{ "category": "display", "name": "Pixel défectueux", "status": "warn", "value": "2 morts en haut à droite" },
{ "category": "battery", "name": "Capacité", "status": "fail", "value": "67%", "detail": { "cycles": 1245 } },
{ "category": "audio", "name": "Haut-parleur principal", "status": "pass" },
{ "category": "audio", "name": "Écouteur", "status": "fail", "value": "silence" },
{ "category": "network", "name": "Wi-Fi", "status": "pass", "value": "-58dBm" },
{ "category": "network", "name": "GPS", "status": "skip" },
{ "category": "sensors", "name": "Accéléromètre", "status": "pass" },
{ "category": "charge", "name": "Connecteur lightning", "status": "warn", "value": "intermittent" }
]
}
Réponse : { inserted: 9 }. Tu peux appeler ce endpoint plusieurs fois sur la même session (test par test ou par lot).
Finaliser la session avec un résumé + rapport PDF
PATCH /api/diagnostic-sessions/:id
{
"summary": "Batterie HS (67%), écouteur muet, écran 2 pixels morts. Réparation possible : batterie + écouteur.",
"reportUrl": "https://app.fixvault.fr/uploads/abc-rapport.pdf",
"finishedAt": "2026-04-16T15:22:00.000Z"
}
Pour le reportUrl : upload d'abord le PDF via POST /api/upload, récupère l'URL retournée et passe-la ici.
Lister les sessions d'un ticket
GET /api/tickets/:id/diagnostic-sessions
Réponse :
{
"items": [
{
"id": "uuid",
"ticketId": "uuid",
"deviceModel": "iPhone15,2",
"osVersion": "17.4.1",
"summary": "...",
"reportUrl": "https://...",
"runAt": "2026-04-16T15:00:00.000Z",
"finishedAt": "2026-04-16T15:22:00.000Z",
"tests": [
{ "id": "...", "category": "display", "name": "Multi-touch", "status": "pass", "value": "10/10 points", "detail": {} }
],
"_count": { "tests": 9 }
}
],
"total": 1
}
Détail session
GET /api/diagnostic-sessions/:id
Supprimer
DELETE /api/diagnostic-sessions/:id
Les DiagnosticTestResult sont supprimés en cascade.
Workflow complet recommandé pour l'outil .exe
- Login JWT (
POST /api/auth/login). - Recherche ticket cible (par code ou via le client) avec
GET /api/service/tickets?keyword=.... - Démarre la session :
POST /api/diagnostic-sessionsavec les infos appareil détectées (ADB/libimobiledevice). - Exécute les tests un par un ou en batch, push les résultats :
POST /api/diagnostic-sessions/:id/tests. - Génère le rapport PDF côté outil + upload via
POST /api/upload. - Finalise :
PATCH /api/diagnostic-sessions/:idavecsummary,reportUrl,finishedAt. - (Optionnel) Change le statut ticket :
PATCH /api/tickets/:id/status→WAITING_QUOTEouREADYselon le diagnostic.
Catégories suggérées pour category
display, touch, battery, charge, audio, camera, sensors, network (wifi/cellular/gps/bluetooth/nfc), buttons, boot, storage, system, security, peripherals. Tu peux en inventer d'autres librement, c'est juste un libellé.
Statuts pour status
| Valeur | Sens |
|---|---|
pass | Test réussi |
fail | Test échoué (défaut détecté) |
warn | Anomalie mineure / dégradation à surveiller |
skip | Test non exécuté (incompatible appareil, refusé par l'user, etc.) |
8. Webhooks (optionnel)
Config dans Paramètres → Accès API → Webhooks.
Événements disponibles :
ticket.createdticket.updatedticket.completedinvoice.createdinvoice.paidcustomer.created
Payload POST signé HMAC-SHA256 dans le header X-FixVault-Signature :
{
"event": "ticket.created",
"timestamp": "2026-04-16T12:34:56.000Z",
"tenantId": "uuid",
"data": { "id": "...", "ticketCode": "T-7456", ... }
}
9. Exemples curl (copier-coller)
Login puis créer un ticket
TOKEN=$(curl -sS -X POST https://app.fixvault.fr/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@boutique.fr","password":"xxx"}' | jq -r .accessToken)
curl -sS -X POST https://app.fixvault.fr/api/tickets \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"deviceName":"iPhone 13","manufacturer":"Apple","task":"Diagnostic complet","status":"INTAKE"}'
Attacher un rapport de diagnostic
# Upload
URL=$(curl -sS -X POST https://app.fixvault.fr/api/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@rapport-diag.pdf" | jq -r .url)
# Attacher
curl -sS -X PATCH https://app.fixvault.fr/api/tickets/<ticket-id> \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"testReportUrl\":\"$URL\"}"
Service key (lecture seule)
curl -sS -H "x-service-key: <YOUR_KEY>" \
"https://app.fixvault.fr/api/service/tickets?email=jdupont@example.com"
10. Contact intégration
Pour toute question : e.repare33@gmail.com