Aller au contenu principal

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.com par 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 .env non 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 :

  1. POST /api/auth/login avec { email, password } → retourne { accessToken, refreshToken, user }.
  2. Mettre Authorization: Bearer <accessToken> sur chaque requête.
  3. Si 401, faire POST /api/auth/refresh avec { refreshToken } → nouveau access token.
  4. 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 client
  • customer_id : UUID client OU ancien externalId RepairDesk
  • from_date, to_date : timestamps unix en secondes
  • page (≥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

CodeSignification
200 / 201OK
400Validation échouée (body details.fieldErrors)
401Token absent / invalide / expiré
403Permission refusée
404Ressource introuvable
409Conflit (doublon unique, statut incompatible)
422Transition métier invalide (ex : fermer un ticket déjà livré)
429Rate limit dépassé
500Erreur serveur (à reporter)

Body erreur standard :

{ "error": "Validation failed", "details": { "fieldErrors": { "email": ["Invalid email"] } } }

6. Workflow suggéré pour un outil de diagnostic Android/iOS

  1. Employé ouvre l'outil sur son PC pendant que le client dépose son appareil.
  2. Outil se connecte à FixVault en JWT (login employé + 2FA si activé).
  3. Recherche du client existant ou création à la volée :
    GET /api/customers/check-duplicate?email=...&mobile=...
    POST /api/customers (si absent)
  4. Création du ticket en statut INTAKE avec deviceName + manufacturer + imei.
  5. Détection appareil côté Android/iOS (via ADB / libimobiledevice) → récupération infos système (modèle, OS, batterie, réseau, etc.).
  6. Exécution de tests (écran, multi-touch, haut-parleurs, micro, caméras, capteurs, charge, GPS, Bluetooth, Wi-Fi, NFC, etc.).
  7. Pour chaque test : POST /api/tickets/:id/comments type DIAGNOSTIC + résultat.
  8. Upload des captures/rapport : POST /api/upload → mettre testReportUrl sur le ticket.
  9. Changement de statut final : PATCH /api/tickets/:id/statusDIAGNOSIS, puis WAITING_QUOTE ou READY selon le diagnostic.
  10. 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

  1. Login JWT (POST /api/auth/login).
  2. Recherche ticket cible (par code ou via le client) avec GET /api/service/tickets?keyword=....
  3. Démarre la session : POST /api/diagnostic-sessions avec les infos appareil détectées (ADB/libimobiledevice).
  4. Exécute les tests un par un ou en batch, push les résultats : POST /api/diagnostic-sessions/:id/tests.
  5. Génère le rapport PDF côté outil + upload via POST /api/upload.
  6. Finalise : PATCH /api/diagnostic-sessions/:id avec summary, reportUrl, finishedAt.
  7. (Optionnel) Change le statut ticket : PATCH /api/tickets/:id/statusWAITING_QUOTE ou READY selon 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

ValeurSens
passTest réussi
failTest échoué (défaut détecté)
warnAnomalie mineure / dégradation à surveiller
skipTest 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.created
  • ticket.updated
  • ticket.completed
  • invoice.created
  • invoice.paid
  • customer.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