Démarrage rapide
- Connectez-vous à votre espace client, puis ouvrez Mon compte › Clés API.
- Créez une clé de test : elle commence par
qsk_test_et donne accès à trois véhicules simulés. - Appelez
/mepour vérifier la clé, puis/positions. - Quand votre intégration fonctionne, créez une clé de production (
qsk_live_) : les mêmes appels renvoient vos vrais véhicules.
Adresse de base de toutes les routes :
https://qentrasystem.com/api/public/v1
Vous codez avec un assistant IA ? Donnez-lui le fichier qentra-api.md ou utilisez nos prompts pour Claude, ChatGPT et Gemini.
Premier appel :
curl "https://qentrasystem.com/api/public/v1/me" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/me');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/me', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/me",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Authentification
Chaque requête envoie la clé dans l’en-tête Authorization, en HTTPS uniquement :
Authorization: Bearer qsk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- La clé n’est affichée qu’une fois, à sa création. Nous n’en gardons qu’une empreinte chiffrée : nous ne pouvons pas vous la renvoyer.
- Gardez-la côté serveur (variable d’environnement
QENTRA_API_KEY), jamais dans une application mobile ou une page web. - Une clé peut être limitée à certaines adresses IP, expirer à une date choisie et être révoquée à tout moment.
Test et production
qsk_test_Trois véhicules simulés (ids 9001, 9002 et 9003) qui roulent dans Abidjan, des alertes et une zone d’exemple. Les commandes sont simulées. Quota de 1 000 appels par jour.
qsk_live_Vos vrais véhicules, leurs positions et leurs alertes. Quota mensuel de 20 000 appels par véhicule (au moins 20 000).
Les réponses ont exactement le même format dans les deux modes.
Format des réponses
Toutes les réponses sont en JSON (UTF-8). Les dates sont au format ISO 8601 en UTC, les vitesses en km/h et les distances en kilomètres.
Succès
{
"success": true,
"data": [],
"meta": {
"count": 1
}
}
Erreur
{
"success": false,
"error": {
"code": "vehicle_not_found",
"message": "Véhicule 12 introuvable sur ce compte.",
"request_id": "9b1f4c2e-6a0d-4f7e-8c1b-2d3e4f5a6b7c"
}
}
Le champ request_id (aussi renvoyé dans l’en-tête X-Request-Id) identifie chaque appel : communiquez-le au support pour une recherche immédiate. Les listes paginées renvoient meta.page, meta.per_page, meta.total et meta.last_page.
Limites et quotas
| En-tête | Signification |
|---|---|
| X-RateLimit-Limit | Requêtes autorisées par minute et par clé (60). |
| X-RateLimit-Remaining | Requêtes restantes dans la minute en cours. |
| X-Quota-Limit | Quota du mois (ou du jour en test). |
| X-Quota-Remaining | Appels restants avant la remise à zéro. |
| Retry-After | Secondes à attendre après une erreur rate_limited. |
| X-Request-Id | Identifiant unique de la requête. |
L’historique est limité à 7 jours par appel et à 5 000 points : découpez les périodes plus longues.
Droits des clés
Donnez à chaque clé uniquement les droits dont le logiciel a besoin.
| vehicles:read | Lire les véhicules et leurs positions |
| history:read | Lire l'historique et les rapports |
| alerts:read | Lire les alertes |
| zones:read | Lire les zones |
| shares:write | Créer des liens de partage de position |
| webhooks:manage | Gérer les webhooks |
| commands:engine | Couper et remettre le moteur : exige le mot de passe du compte à la création de la clé, refusé si le véhicule roule. |
Référence des routes
Toutes les adresses ci-dessous sont relatives à https://qentrasystem.com/api/public/v1. Les exemples utilisent les véhicules de test : remplacez 9001 par l’identifiant renvoyé par /vehicles.
| GET | /me | Vérifier la clé |
| GET | /vehicles | Lister les véhicules |
| GET | /vehicles/9001 | Détail d’un véhicule |
| GET | /positions | Dernières positions |
| GET | /vehicles/9001/history | Historique des positions |
| GET | /vehicles/9001/trips | Trajets |
| GET | /vehicles/9001/stops | Arrêts |
| GET | /vehicles/9001/summary | Résumé d’activité |
| GET | /alerts | Alertes |
| GET | /zones | Zones |
| POST | /vehicles/9001/shares | Partager une position |
| POST | /vehicles/9001/engine/stop | Couper le moteur |
| POST | /vehicles/9001/engine/resume | Remettre le moteur en marche |
| GET | /webhooks | Lister les webhooks |
| POST | /webhooks | Créer un webhook |
| POST | /webhooks/3/test | Tester un webhook |
| GET | /webhooks/3/deliveries | Historique des envois |
| DELETE | /webhooks/3 | Supprimer un webhook |
Compte
Vérifier la clé
/me
Renvoie le compte, le mode (test ou production), les droits de la clé et la consommation du quota. Idéal pour tester votre configuration.
Requête
curl "https://qentrasystem.com/api/public/v1/me" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/me');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/me', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/me",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"account": {
"id": 42,
"name": "Transports Kouassi",
"company": "TK SARL"
},
"key": {
"name": "ERP livraison",
"prefix": "qsk_live_8fK2pQ",
"mode": "live",
"scopes": [
"vehicles:read",
"history:read"
],
"expires_at": null
},
"quota": {
"period": "month",
"limit": 200000,
"used": 1840,
"resets_at": "2026-11-01T00:00:00+00:00"
},
"rate_limit_per_minute": 60
}
}
Véhicules et positions
Lister les véhicules
/vehicles
Droit : vehicles:read
Tous les véhicules du compte, avec leur état de connexion. Utilisez /positions pour obtenir aussi la dernière position.
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"id": 9001,
"name": "Véhicule de test 1",
"plate": "AB-1234-CI",
"imei": "860000000009001",
"model": "Simulateur",
"vehicle_type": "car",
"status": "online",
"last_update": "2026-10-01T14:20:05Z"
}
],
"meta": {
"count": 1
}
}
Détail d’un véhicule
/vehicles/9001
Droit : vehicles:read
Un véhicule et sa dernière position connue.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| id | entier | Oui | Identifiant du véhicule (dans l’adresse). |
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles/9001" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles/9001",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"id": 9001,
"name": "Véhicule de test 1",
"plate": "AB-1234-CI",
"imei": "860000000009001",
"model": "Simulateur",
"vehicle_type": "car",
"status": "online",
"last_update": "2026-10-01T14:20:05Z",
"position": {
"latitude": 5.341224,
"longitude": -4.017311,
"speed_kmh": 40,
"course": 212,
"altitude": 12,
"fix_time": "2026-10-01T14:20:05Z",
"valid": true,
"ignition": true,
"motion": true,
"battery_level": 100,
"power_volts": 12.6,
"odometer_km": 15230.4
}
}
}
Dernières positions
/positions
Droit : vehicles:read
La dernière position de tous les véhicules en un seul appel. Pour un suivi en continu, interrogez cette route toutes les 15 à 30 secondes, ou utilisez les webhooks pour les alertes.
Requête
curl "https://qentrasystem.com/api/public/v1/positions" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/positions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/positions', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/positions",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"id": 9001,
"name": "Véhicule de test 1",
"status": "online",
"position": {
"latitude": 5.341224,
"longitude": -4.017311,
"speed_kmh": 40,
"course": 212,
"fix_time": "2026-10-01T14:20:05Z",
"ignition": true
}
}
],
"meta": {
"count": 1
}
}
Historique et rapports
Historique des positions
/vehicles/9001/history
Droit : history:read
Tous les points GPS d’une période (7 jours au plus, 5 000 points au plus).
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| from | date ISO 8601 | Oui | Début de la période (UTC conseillé). |
| to | date ISO 8601 | Oui | Fin de la période, postérieure à from. |
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles/9001/history?from=2026-10-01T06:00:00Z&to=2026-10-01T12:00:00Z" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/history?from=2026-10-01T06:00:00Z&to=2026-10-01T12:00:00Z');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/history?from=2026-10-01T06:00:00Z&to=2026-10-01T12:00:00Z', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles/9001/history?from=2026-10-01T06:00:00Z&to=2026-10-01T12:00:00Z",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"latitude": 5.336401,
"longitude": -4.008712,
"speed_kmh": 38,
"course": 95,
"altitude": 12,
"fix_time": "2026-10-01T06:00:00Z",
"valid": true,
"ignition": true,
"motion": true,
"battery_level": 100,
"power_volts": 12.6,
"odometer_km": 15190.2
}
],
"meta": {
"vehicle_id": 9001,
"from": "2026-10-01T06:00:00Z",
"to": "2026-10-01T12:00:00Z",
"count": 361
}
}
Trajets
/vehicles/9001/trips
Droit : history:read
Les trajets de la période, avec départ, arrivée, durée, distance et vitesses.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| from | date ISO 8601 | Oui | Début de la période. |
| to | date ISO 8601 | Oui | Fin de la période. |
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles/9001/trips?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/trips?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/trips?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles/9001/trips?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"start_time": "2026-10-01T07:12:00Z",
"end_time": "2026-10-01T07:57:00Z",
"duration_seconds": 2700,
"distance_km": 18.4,
"average_speed_kmh": 32,
"max_speed_kmh": 61,
"start": {
"latitude": 5.3364,
"longitude": -4.0267,
"address": "Plateau, Abidjan"
},
"end": {
"latitude": 5.3464,
"longitude": -4.0167,
"address": "Cocody, Abidjan"
}
}
],
"meta": {
"vehicle_id": 9001,
"count": 1
}
}
Arrêts
/vehicles/9001/stops
Droit : history:read
Les arrêts de la période, avec leur durée et leur position.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| from | date ISO 8601 | Oui | Début de la période. |
| to | date ISO 8601 | Oui | Fin de la période. |
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles/9001/stops?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/stops?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/stops?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles/9001/stops?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"start_time": "2026-10-01T08:00:00Z",
"end_time": "2026-10-01T08:15:00Z",
"duration_seconds": 900,
"latitude": 5.3464,
"longitude": -4.0167,
"address": "Cocody, Abidjan"
}
],
"meta": {
"vehicle_id": 9001,
"count": 1
}
}
Résumé d’activité
/vehicles/9001/summary
Droit : history:read
Distance, vitesses et heures moteur de la période, ou jour par jour avec daily=1.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| from | date ISO 8601 | Oui | Début de la période. |
| to | date ISO 8601 | Oui | Fin de la période. |
| daily | 0 ou 1 | Non | 1 : un résumé par jour. |
Requête
curl "https://qentrasystem.com/api/public/v1/vehicles/9001/summary?from=2026-09-24T00:00:00Z&to=2026-10-01T00:00:00Z&daily=1" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/summary?from=2026-09-24T00:00:00Z&to=2026-10-01T00:00:00Z&daily=1');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/summary?from=2026-09-24T00:00:00Z&to=2026-10-01T00:00:00Z&daily=1', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/vehicles/9001/summary?from=2026-09-24T00:00:00Z&to=2026-10-01T00:00:00Z&daily=1",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"date": "2026-09-24T00:00:00Z",
"distance_km": 42.7,
"average_speed_kmh": 31,
"max_speed_kmh": 64,
"engine_hours": 2.4
}
],
"meta": {
"vehicle_id": 9001,
"count": 7
}
}
Alertes et zones
Alertes
/alerts
Droit : alerts:read
Les alertes du compte, de la plus récente à la plus ancienne, avec pagination.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| status | open, resolved ou all | Non | Filtre sur l’état (all par défaut). |
| vehicle_id | entier | Non | Un seul véhicule. |
| type | texte | Non | Type d’alerte, par exemple powerCut, overspeed, geofenceExit. |
| from / to | date ISO 8601 | Non | Période de survenue. |
| per_page | 1 à 100 | Non | 50 par défaut. |
| page | entier | Non | Numéro de page. |
Requête
curl "https://qentrasystem.com/api/public/v1/alerts?status=open&per_page=50" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/alerts?status=open&per_page=50');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/alerts?status=open&per_page=50', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/alerts?status=open&per_page=50",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"id": 1,
"vehicle_id": 9001,
"type": "geofenceExit",
"label": "Sortie d'une zone",
"level": "warning",
"latitude": 5.3501,
"longitude": -4.0102,
"occurrences": 1,
"occurred_at": "2026-10-01T14:08:00Z",
"last_seen_at": "2026-10-01T14:08:00Z",
"resolved": false,
"resolved_at": null
}
],
"meta": {
"page": 1,
"per_page": 50,
"total": 1,
"last_page": 1
}
}
Zones
/zones
Droit : zones:read
Les zones (géorepérage) du compte et les véhicules qui y sont rattachés.
Requête
curl "https://qentrasystem.com/api/public/v1/zones" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/zones');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/zones', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/zones",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"id": 1,
"name": "Dépôt de test",
"type": "circle",
"geometry": "CIRCLE (5.3364 -4.0267, 500)",
"alert_on_enter": true,
"alert_on_exit": true,
"vehicle_ids": [
9001,
9002,
9003
]
}
],
"meta": {
"count": 1
}
}
Partage
Commandes moteur
Couper le moteur
/vehicles/9001/engine/stop
Droit : commands:engine
Immobilise le véhicule. Refusé si le véhicule roule à plus de 10 km/h. Le propriétaire est notifié et un webhook command.sent est envoyé.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| confirm | true | Oui | Confirmation explicite obligatoire. |
Requête
curl -X POST "https://qentrasystem.com/api/public/v1/vehicles/9001/engine/stop" \
-H "Authorization: Bearer $QENTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm":true}'
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/engine/stop');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => '{"confirm":true}',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/engine/stop', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({"confirm":true}),
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"POST",
"https://qentrasystem.com/api/public/v1/vehicles/9001/engine/stop",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
json={"confirm":True},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"status": "sent",
"message": "Commande envoyée au boîtier.",
"command": "engine_stop",
"vehicle_id": 9001
}
}
Remettre le moteur en marche
/vehicles/9001/engine/resume
Droit : commands:engine
Lève l’immobilisation. Le propriétaire est notifié.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| confirm | true | Oui | Confirmation explicite obligatoire. |
Requête
curl -X POST "https://qentrasystem.com/api/public/v1/vehicles/9001/engine/resume" \
-H "Authorization: Bearer $QENTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm":true}'
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/vehicles/9001/engine/resume');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => '{"confirm":true}',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/vehicles/9001/engine/resume', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({"confirm":true}),
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"POST",
"https://qentrasystem.com/api/public/v1/vehicles/9001/engine/resume",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
json={"confirm":True},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"status": "sent",
"message": "Commande envoyée au boîtier.",
"command": "engine_resume",
"vehicle_id": 9001
}
}
Webhooks
Lister les webhooks
/webhooks
Droit : webhooks:manage
Les webhooks du compte et l’état de leur dernier envoi.
Requête
curl "https://qentrasystem.com/api/public/v1/webhooks" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/webhooks');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/webhooks', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"id": 3,
"url": "https://mon-logiciel.ci/webhooks/qentra",
"events": [
"alert.created",
"alert.resolved"
],
"active": true,
"failures": 0,
"last_status": 200,
"last_delivery_at": "2026-10-01T14:08:02Z",
"created_at": "2026-09-30T10:00:00Z"
}
]
}
Créer un webhook
/webhooks
Droit : webhooks:manage
Enregistre une adresse HTTPS publique. Le secret de signature n’est renvoyé qu’à la création.
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| url | adresse HTTPS | Oui | Adresse de votre serveur (les adresses internes sont refusées). |
| events | liste | Oui | alert.created, alert.resolved, command.sent. |
Requête
curl -X POST "https://qentrasystem.com/api/public/v1/webhooks" \
-H "Authorization: Bearer $QENTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://mon-logiciel.ci/webhooks/qentra","events":["alert.created","alert.resolved"]}'
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/webhooks');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => '{"url":"https://mon-logiciel.ci/webhooks/qentra","events":["alert.created","alert.resolved"]}',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({"url":"https://mon-logiciel.ci/webhooks/qentra","events":["alert.created","alert.resolved"]}),
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"POST",
"https://qentrasystem.com/api/public/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
json={"url":"https://mon-logiciel.ci/webhooks/qentra","events":["alert.created","alert.resolved"]},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"id": 3,
"url": "https://mon-logiciel.ci/webhooks/qentra",
"events": [
"alert.created",
"alert.resolved"
],
"active": true,
"failures": 0,
"last_status": null,
"last_delivery_at": null,
"created_at": "2026-10-01T14:20:00Z",
"secret": "whsec_…"
},
"meta": {
"notice": "Conservez ce secret : il ne sera plus jamais affiché."
}
}
Tester un webhook
/webhooks/3/test
Droit : webhooks:manage
Envoie un événement test.ping signé à votre adresse.
Requête
curl -X POST "https://qentrasystem.com/api/public/v1/webhooks/3/test" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/webhooks/3/test');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/webhooks/3/test', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"POST",
"https://qentrasystem.com/api/public/v1/webhooks/3/test",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"event_id": "5b0c7e2a-1f7d-4a38-9a51-0c8a2b7d1e44",
"status": "queued"
}
}
Historique des envois
/webhooks/3/deliveries
Droit : webhooks:manage
Les 50 derniers envois d’un webhook, avec le code HTTP reçu.
Requête
curl "https://qentrasystem.com/api/public/v1/webhooks/3/deliveries" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/webhooks/3/deliveries');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/webhooks/3/deliveries', {
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"GET",
"https://qentrasystem.com/api/public/v1/webhooks/3/deliveries",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": [
{
"event_id": "5b0c7e2a-1f7d-4a38-9a51-0c8a2b7d1e44",
"event": "test.ping",
"status": "success",
"attempts": 1,
"response_code": 200,
"error": null,
"created_at": "2026-10-01T14:20:01Z",
"delivered_at": "2026-10-01T14:20:02Z"
}
]
}
Supprimer un webhook
/webhooks/3
Droit : webhooks:manage
Arrête définitivement les envois vers cette adresse.
Requête
curl -X DELETE "https://qentrasystem.com/api/public/v1/webhooks/3" \
-H "Authorization: Bearer $QENTRA_API_KEY"
<?php
$ch = curl_init('https://qentrasystem.com/api/public/v1/webhooks/3');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('QENTRA_API_KEY'),
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (! $response['success']) {
exit($response['error']['code'] . ' : ' . $response['error']['message']);
}
print_r($response['data']);
const response = await fetch('https://qentrasystem.com/api/public/v1/webhooks/3', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.QENTRA_API_KEY}`,
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.success) {
throw new Error(`${json.error.code} : ${json.error.message}`);
}
console.log(json.data);
import os
import requests
response = requests.request(
"DELETE",
"https://qentrasystem.com/api/public/v1/webhooks/3",
headers={"Authorization": f"Bearer {os.environ['QENTRA_API_KEY']}"},
timeout=15,
)
data = response.json()
if not data["success"]:
raise Exception(f"{data['error']['code']} : {data['error']['message']}")
print(data["data"])
Réponse
{
"success": true,
"data": {
"deleted": true,
"id": 3
}
}
Webhooks
Plutôt que d’interroger l’API en boucle, recevez les événements sur votre serveur dès qu’ils se produisent. Chaque envoi est une requête POST en JSON vers votre adresse HTTPS, signée avec le secret du webhook.
| Événement | Envoyé quand |
|---|---|
| alert.created | Nouvelle alerte |
| alert.resolved | Alerte résolue |
| command.sent | Commande moteur envoyée |
| test.ping | Envoi de test demandé par la route /webhooks/{id}/test |
Contenu envoyé
{
"id": "0f6e2c1a-8d4b-4a1e-9b7c-3e2d1f0a9b8c",
"type": "alert.created",
"created_at": "2026-10-01T14:08:01Z",
"data": {
"id": 128,
"vehicle_id": 9001,
"type": "powerCut",
"label": "Coupure d'alimentation",
"level": "critical",
"latitude": 5.3501,
"longitude": -4.0102,
"occurrences": 1,
"occurred_at": "2026-10-01T14:08:00Z",
"last_seen_at": "2026-10-01T14:08:00Z",
"resolved": false,
"resolved_at": null,
"vehicle": {
"id": 9001,
"name": "Véhicule de test 1",
"plate": "AB-1234-CI"
}
}
}
En-têtes
| Qentra-Signature | t=1759327681,v1=5f2b… : horodatage et signature HMAC-SHA256. |
| Qentra-Event | Type de l’événement, par exemple alert.created. |
| Qentra-Delivery | Identifiant unique de l’envoi : servez-vous-en pour ignorer un doublon. |
| Content-Type | application/json |
| User-Agent | Qentra-Webhooks/1.0 |
Vérifier la signature
Calculez HMAC-SHA256(secret, t + "." + corps brut) en hexadécimal et comparez-le à v1 avec une comparaison à temps constant. Refusez les envois de plus de 5 minutes pour bloquer les rejeux. Utilisez toujours le corps brut, avant tout décodage JSON.
<?php
// Laravel, Symfony ou PHP natif : lire le corps brut, avant tout décodage
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_QENTRA_SIGNATURE'] ?? '';
$secret = getenv('QENTRA_WEBHOOK_SECRET'); // whsec_...
parse_str(str_replace(',', '&', $header), $parts); // t=...&v1=...
$expected = hash_hmac('sha256', $parts['t'] . '.' . $payload, $secret);
if (! hash_equals($expected, $parts['v1'] ?? '') || abs(time() - (int) $parts['t']) > 300) {
http_response_code(400);
exit('Signature invalide');
}
$event = json_decode($payload, true);
// $event['type'] : alert.created, alert.resolved, command.sent, test.ping
http_response_code(200);
// Node.js + Express : garder le corps brut pour vérifier la signature
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/webhooks/qentra', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('Qentra-Signature') || '';
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', process.env.QENTRA_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest('hex');
const valid = parts.v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
if (!valid || Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) {
return res.status(400).send('Signature invalide');
}
const event = JSON.parse(req.body);
console.log(event.type, event.data);
res.sendStatus(200);
});
app.listen(3000);
# Flask : lire le corps brut avant de le décoder
import hashlib, hmac, json, os, time
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/webhooks/qentra")
def qentra_webhook():
payload = request.get_data()
parts = dict(p.split("=", 1) for p in request.headers.get("Qentra-Signature", "").split(",") if "=" in p)
expected = hmac.new(
os.environ["QENTRA_WEBHOOK_SECRET"].encode(),
f"{parts.get('t')}.".encode() + payload,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, parts.get("v1", "")) or abs(time.time() - int(parts.get("t", 0))) > 300:
abort(400, "Signature invalide")
event = json.loads(payload)
print(event["type"], event["data"])
return "", 200
Réponse attendue et nouvelles tentatives
- Répondez par un code 2xx en moins de 10 secondes, puis traitez l’événement en arrière-plan.
- Sinon, l’envoi est retenté 5 fois : après 1 minute, 5 minutes, 30 minutes, 2 heures puis 6 heures.
- Après 20 échecs d’affilée, le webhook est désactivé. Réactivez-le depuis votre espace une fois votre serveur réparé.
- Un même événement peut arriver deux fois : dédoublonnez avec
Qentra-Delivery. - Seules les adresses HTTPS publiques sont acceptées (pas d’adresse locale ni privée). 5 webhooks au plus par compte.
Codes d’erreur
En cas d’erreur, success vaut false et error.code contient l’un des codes ci-dessous. Basez votre logique sur ce code, pas sur le message, qui peut changer.
| Code | HTTP | Signification | Que faire |
|---|---|---|---|
| missing_api_key | 401 | En-tête Authorization absent. | Ajoutez « Authorization: Bearer qsk_… ». |
| invalid_api_key | 401 | Clé inconnue ou mal recopiée. | Vérifiez la clé, sans espace ni retour à la ligne. |
| revoked_api_key | 401 | Clé révoquée. | Créez une nouvelle clé dans votre espace. |
| expired_api_key | 401 | Clé expirée. | Créez une nouvelle clé dans votre espace. |
| account_disabled | 403 | Compte désactivé. | Contactez le service client. |
| ip_not_allowed | 403 | Appel depuis une IP non autorisée pour cette clé. | Ajoutez l’IP de votre serveur à la clé. |
| insufficient_scope | 403 | La clé n’a pas le droit nécessaire (indiqué dans required_scope). | Créez une clé avec ce droit. |
| forbidden | 403 | Le compte n’est pas autorisé à cette action. | Demandez l’autorisation à votre gestionnaire. |
| vehicle_not_found | 404 | Véhicule inconnu ou absent de ce compte. | Utilisez un id renvoyé par /vehicles. |
| webhook_not_found | 404 | Webhook inconnu. | Utilisez un id renvoyé par /webhooks. |
| route_not_found | 404 | Adresse ou méthode inconnue. | Vérifiez l’adresse et la méthode (GET, POST, DELETE). |
| vehicle_moving | 409 | Coupure moteur refusée : le véhicule roule (vitesse dans speed_kmh). | Réessayez quand le véhicule est à l’arrêt. |
| validation_failed | 422 | Paramètre manquant ou invalide (détail par champ dans fields). | Corrigez les champs indiqués. |
| period_too_long | 422 | Période supérieure au maximum (max_days). | Découpez la période. |
| too_many_points | 422 | Trop de points GPS sur la période. | Réduisez la période. |
| invalid_webhook_url | 422 | Adresse refusée : pas en HTTPS, interne ou introuvable. | Utilisez une adresse HTTPS publique. |
| limit_reached | 422 | Nombre maximal de webhooks atteint. | Supprimez un webhook inutilisé. |
| share_limit_reached | 422 | Trop de liens de partage actifs pour ce véhicule. | Révoquez un lien existant. |
| command_unavailable | 422 | Commande non disponible sur la plateforme. | Contactez le service client. |
| service_required | 402 | Service non souscrit pour ce véhicule (option indiquée dans feature). | Activez le service dans votre espace, rubrique Abonnement. |
| rate_limited | 429 | Plus de 60 requêtes par minute (attente dans retry_after). | Attendez le délai indiqué par l’en-tête Retry-After. |
| quota_exceeded | 429 | Quota du mois (ou du jour en test) atteint (reprise dans resets_at). | Attendez la remise à zéro ou contactez-nous. |
| command_failed | 502 | Le boîtier n’a pas accepté la commande. | Vérifiez que le boîtier est en ligne, puis réessayez. |
| gps_unavailable | 503 | Serveur GPS momentanément indisponible. | Réessayez après quelques secondes. |
Exemples de réponses d’erreur :
422 validation_failed
{
"success": false,
"error": {
"code": "validation_failed",
"message": "Paramètres invalides.",
"fields": {
"from": [
"Le champ from est obligatoire."
]
},
"request_id": "c3a1…"
}
}
429 rate_limited
{
"success": false,
"error": {
"code": "rate_limited",
"message": "Trop de requêtes, réessayez dans 12 secondes.",
"retry_after": 12,
"request_id": "d4b2…"
}
}
Pour les erreurs 429, 502 et 503, réessayez avec un délai croissant (1 s, 2 s, 4 s…). Ne réessayez pas automatiquement les erreurs 401, 403, 404 et 422 : la même requête échouerait encore.
Bonnes pratiques
- Une clé par logiciel, avec seulement les droits utiles : si une clé fuit, vous la révoquez sans couper les autres.
- Limitez les clés de production aux adresses IP de vos serveurs.
- Ne mettez jamais une clé dans une application mobile, un site public ou un dépôt Git. Appelez l’API depuis votre serveur.
- Remplacez vos clés régulièrement : créez la nouvelle, déployez-la, puis révoquez l’ancienne.
- Réservez le droit
commands:engineaux logiciels qui en ont vraiment besoin et journalisez chaque coupure de votre côté. - Surveillez la page « Clés API » de votre espace : elle montre les derniers appels, leur code de réponse et leur identifiant.
Vous pensez avoir trouvé une faille ? Écrivez à service@qentrasystem.com avant toute publication.
Versions
La version actuelle est v1, indiquée dans l’adresse (/api/public/v1). Dans une même version, nous ajoutons des champs et des routes sans rien retirer : ignorez les champs que vous ne connaissez pas. Un changement incompatible donnera lieu à une v2, annoncée par e-mail au moins 6 mois avant l’arrêt de la v1.
Une question sur l’intégration ?
Indiquez l’identifiant de requête (X-Request-Id) concerné : nous retrouvons l’appel immédiatement.
Contacter l’équipe technique