API DECI : importer les points d'eau incendie d'un SDIS
Vos points d'eau dans KiloDelta, sans rien ressaisir.
Je suis sapeur-pompier dans le Vaucluse et je développe KiloDelta seul. Une colonne qui monte en renfort sur un feu de forêt arrive dans un département dont elle ne connaît ni les poteaux ni les réserves. Cette API permet à votre service SIG d'envoyer son catalogue DECI en une requête. Les PEI apparaissent ensuite dans l'application, avec leur état et leur débit.
- Une clé par SDIS, rattachée à votre service, qui ne sait faire qu'une chose : écrire vos PEI.
- Un
POSTJSON par département. Chaque envoi remplace le catalogue précédent. - Quatre champs obligatoires par PEI :
ref,lat,lon,type. Envoyez aussietat, sinon le PEI est affiché indisponible. - Votre SDIS publie déjà ses PEI en open data ? Donnez-moi l'adresse, je les récupère moi-même.
Ce que vos PEI deviennent dans l'application.
Un pompier ouvre la carte, il voit vos points d'eau. Il n'a rien à configurer : l'application télécharge les PEI du département quand il déplace la carte. Voici où ils servent.
Sur la carte
Une forme par type d'ouvrage, une bordure pour l'état, une couleur pour le débit ou le volume. Au zoom fort, un capuchon indique le réseau.
La fiche au tap
État, débit à 1 bar, pressions, volume, commune, adresse. Un bandeau signale un PEI privé ou en emploi restreint.
Dès l'accueil
Le point d'eau le plus proche s'affiche sous la météo, avec sa distance. Un tap et la carte se cale dessus.
Sans réseau
Un département chargé reste consultable 30 jours hors ligne. Vos PEI entrent aussi dans le pack hors ligne du département.
Défendabilité
Le calcul de défendabilité des maisons regarde le débit réel des PEI autour. Sans débit, il prend une valeur médiane.
Au poignet
Le cadran KiloDelta pour montre Garmin peut demander les points d'eau autour de sa position.
Deux façons d'arriver sur la carte
Vous envoyez
Votre SIG exporte le catalogue au format décrit plus bas et l'envoie avec votre clé, à la fréquence que vous voulez. C'est vous qui décidez quand la donnée change.
Je viens chercher
Si vos PEI sont déjà publiés en open data, j'écris l'adaptateur et je les récupère chaque semaine. C'est le cas aujourd'hui sur Hydraclic, PIGMA, GeoBretagne, data.calvados.fr et Ma carte IGN.
Les départements déjà dans l'application.
Cette liste est lue en base à chaque affichage de la page. Elle compte les PEI actifs, ceux qui ont été retirés par un envoi ou une mise à jour ne sont plus comptés.
- Seine-Maritime 76 21 274
- Finistère 29 20 210
- Ille-et-Vilaine 35 GeoBretagne 17 225
- Calvados 14 data.calvados.fr 14 701
- Pyrénées-Atlantiques 64 PIGMA 14 472
- Charente-Maritime 17 Hydraclic 13 280
- Vaucluse 84 Hydraclic 13 217
- Saône-et-Loire 71 13 207
- Côte-d'Or 21 11 883
- Morbihan 56 11 632
- Deux-Sèvres 79 11 535
- Vienne 86 PIGMA 11 287
- Côtes-d'Armor 22 10 828
- Jura 39 7 969
- Dordogne 24 PIGMA 7 200
- Tarn 81 6 699
- Charente 16 PIGMA 6 411
- Haute-Loire 43 Ma carte IGN 6 105
- Tarn-et-Garonne 82 4 762
- Total 223 897
Le vôtre n'y est pas ? Demandez une clé, ou envoyez-moi l'adresse de votre jeu de données s'il est public. Un département ne peut avoir qu'une source : s'il est déjà alimenté depuis une plateforme publique et que vous préférez envoyer vous-même, dites-le dans la demande, je fais la bascule à la main.
Une clé par SDIS, qui ne sert qu'à ça.
- Vous faites la demande. Le formulaire s'ouvre avec le module « DECI » déjà choisi et un message à compléter : nom du SDIS, département(s), e-mail technique, et l'adresse de vos données si elles sont déjà publiques.
-
Je crée la clé. Elle est rattachée à votre SDIS et ne porte qu'un droit,
deci:import. Elle écrit dans une source réservée à votre service, jamais mélangée aux autres. Elle ne peut pas toucher aux PEI d'un autre SDIS. -
Je vous la transmets. Elle a la forme
kd_live_<12 caractères hexa>.<40 caractères hexa>. Je ne garde que l'empreinte du secret (SHA-256) : si vous la perdez, je ne peux pas la retrouver, j'en crée une autre. - Vous testez votre fichier ici, avec l'outil plus bas, avant le premier envoi.
Une clé qui a fuité se désactive côté serveur : elle est alors refusée avec un 401. Prévenez-moi et je vous en donne une nouvelle.
Un seul appel, tout le département.
POST https://kilodelta.fr/api/v1/deci/import X-Api-Key: kd_live_<id>.<secret> Content-Type: application/json
curl -X POST https://kilodelta.fr/api/v1/deci/import \ -H "X-Api-Key: $KD_DECI_KEY" \ -H "Content-Type: application/json" \ --data-binary @peis.json
import os, requests with open("peis.json", "rb") as f: body = f.read() # octets UTF-8, tels quels r = requests.post( "https://kilodelta.fr/api/v1/deci/import", data=body, headers={"X-Api-Key": os.environ["KD_DECI_KEY"], "Content-Type": "application/json"}, timeout=300, ) print(r.status_code, r.json())
Ce qu'il faut savoir avant d'appuyer sur Entrée
Un envoi remplace tout
Les PEI de votre source absents du corps sont retirés. Il n'y a donc pas de pagination : un département part en un seul envoi. Deux envois partiels à la suite, et le second efface le premier.
Le garde-fou
Si aucun PEI du corps n'est valide (corps vide, ou tout rejeté), rien n'est retiré. La réponse le signale avec empty_no_delete.
UTF-8, sans BOM
Un corps qui n'est pas en UTF-8 est refusé avant d'être lu (400). Un BOM en tête fait échouer la lecture du JSON (422). Les vieux exports Latin-1 sont les premiers suspects.
Une fois par jour suffit
Chaque envoi change la version du département et relance le téléchargement dans les applications. Envoyez après une mise à jour de votre base, pas en boucle.
Il n'y a pas de limite de débit sur cet endpoint et le traitement est synchrone : la réponse arrive quand tout est écrit. Prévoyez un délai d'attente large côté client. Pour le premier envoi d'un gros catalogue, prévenez-moi, je regarde passer l'import.
Un objet par PEI, à plat.
Le corps est un objet JSON avec un tableau peis. Un tableau seul à la racine ([ {…}, {…} ]) est
aussi accepté. À la racine, code_dept est facultatif : s'il manque, le département est déduit des deux
premiers caractères du commune_code du premier PEI valide.
Corse et outre-mer : renseignez toujours code_dept. La déduction sur deux caractères donne bien
2A ou 2B, mais 97 pour un code INSEE en 971 ou 974.
| Champ | Type | Limite | Rôle |
|---|---|---|---|
ref requis | texte | 32 car. | Identifiant métier du PEI (ex. INSEE + numéro d'ordre). Sert de clé de mise à jour quand id est absent. |
lat requis | nombre | 6 décimales | Latitude WGS84 en degrés décimaux (EPSG:4326). Pas de Lambert 93. |
lon requis | nombre | 6 décimales | Longitude WGS84 en degrés décimaux. |
type requis | texte | liste | Type d'ouvrage, voir le tableau suivant. Un type inconnu fait ignorer le PEI. |
etat recommandé | texte | liste | disponible, emploi_restreint ou indisponible. Absent ou inconnu : le PEI est enregistré indisponible. |
id | entier | 0 à 4 294 967 295 | Identifiant entier stable de votre SIG (gid, objectid…). S'il est fourni, c'est lui la clé de mise à jour. |
commune_code | texte | 5 car. | Code INSEE de la commune. Sert à déduire le département. |
commune_nom | texte | 120 car. | Nom de la commune, affiché dans la fiche. |
debit_1bar | nombre | entier, m³/h | Débit à 1 bar. Fixe la couleur des poteaux, bouches, poteaux relais, bornes agricoles. |
pression | nombre | bar, 2 déc. | Pression dynamique. |
pression_statique | nombre | bar, 2 déc. | Pression statique. À 8 bar ou plus, le capuchon passe en jaune (réseau surpressé). |
volume_pa | nombre | entier, m³ | Volume utile. Fixe la couleur des citernes, points d'aspiration, PENA, colonnes d'aspiration. |
adresse | texte | 255 car. | Voie ou lieu-dit. street_address est accepté à la place. |
domaine | texte | Une valeur qui commence par « priv » donne un PEI privé, tout le reste un PEI public. status est accepté à la place. |
Les nombres peuvent être envoyés en texte ("60"), ils sont lus pareil. Les champs en trop sont ignorés.
Le type et l'état ne tiennent pas compte de la casse ni des espaces autour.
Les 9 types d'ouvrage
Valeur de type | Dans l'application | Couleur selon | Marqueur |
|---|---|---|---|
poteau |
Poteau | débit | rond |
bouche |
Bouche | débit | losange |
poteau_relais |
Poteau relais | débit | anneau pointillé |
borne_agricole |
Borne agricole | débit | pentagone |
colonne_seche |
Colonne sèche | débit | rectangle étroit |
citerne |
Citerne / bâche | volume | carré |
point_aspiration |
Point d'aspiration | volume | triangle |
pena_baignable |
PENA | volume | triangle inversé |
colonne_aspiration |
Colonne fixe d'aspiration | volume | hexagone |
La clé de mise à jour
D'un envoi à l'autre, un PEI est reconnu par son id si vous le fournissez, sinon par une empreinte
calculée sur ref. Choisissez une règle et gardez-la : passer de l'une à l'autre fait retirer puis recréer
tout le catalogue. Si votre SIG a un identifiant entier stable, donnez-le dans id. Sur un gros catalogue,
c'est plus sûr que l'empreinte, qui peut exceptionnellement tomber deux fois sur la même valeur.
L'outil de test plus bas signale ces doublons.
Un PEI retiré puis renvoyé plus tard revient avec le même identifiant côté KiloDelta.
Trois PEI, trois cas.
Un poteau complet, une bouche en emploi restreint sans débit connu, une citerne privée. Les valeurs sont inventées pour l'exemple.
{
"code_dept": "26",
"peis": [
{
"ref": "26362-0147",
"id": 90147,
"commune_code": "26362",
"commune_nom": "Valence",
"lat": 44.933412,
"lon": 4.892307,
"type": "poteau",
"etat": "disponible",
"debit_1bar": 72,
"pression": 1.4,
"pression_statique": 4.2,
"adresse": "Avenue Victor Hugo",
"domaine": "public"
},
{
"ref": "26362-0588",
"id": 90588,
"commune_code": "26362",
"commune_nom": "Valence",
"lat": 44.915871,
"lon": 4.921654,
"type": "bouche",
"etat": "emploi_restreint",
"adresse": "Rue Faventines",
"domaine": "public"
},
{
"ref": "26362-R012",
"id": 91012,
"commune_code": "26362",
"commune_nom": "Valence",
"lat": 44.94712,
"lon": 4.905581,
"type": "citerne",
"etat": "disponible",
"volume_pa": 120,
"domaine": "privé"
}
]
}
Depuis un export GeoJSON
L'endpoint ne prend pas de GeoJSON directement. Si votre SIG exporte une FeatureCollection de points en EPSG:4326,
une ligne de jq fait la conversion. Adaptez les noms de propriétés à votre export.
jq '{code_dept: "26", peis: [.features[] | {
ref: .properties.numero,
commune_code: .properties.insee,
lat: .geometry.coordinates[1],
lon: .geometry.coordinates[0],
type: .properties.type_kd,
etat: .properties.etat_kd,
debit_1bar: .properties.debit
}]}' export.geojson > peis.json
Dans un GeoJSON, l'ordre est longitude puis latitude. Les valeurs de type et
d'etat doivent être celles des listes ci-dessus : si votre base utilise des codes (PI, BI, PA…),
faites la correspondance dans votre requête d'export.
Voyez le résultat avant d'envoyer.
Déposez votre fichier ou collez son contenu. L'outil applique les mêmes règles que le serveur et vous montre ce qui serait retenu, ignoré, et comment vos PEI s'afficheraient. Rien ne quitte votre navigateur : aucune requête n'est faite, aucune clé n'est demandée.
Le résultat s'affiche ici.
Un compte rendu, pas un simple « OK ».
Un envoi accepté répond 200 avec le bilan de l'import. Par exemple, pour un deuxième envoi :
{
"ok": true,
"source_id": 57,
"code_dept": "26",
"received": 4812,
"added": 6,
"updated": 4801,
"removed": 3,
"skipped": 5,
"reasons": {
"bad_coords": 2,
"unknown_type": 3
},
"unknown_types": [
"hydrant_prive"
],
"unknown_etats": []
}
| Champ | Sens |
|---|---|
source_id | La source réservée à votre SDIS pour ce département. Elle est créée au premier envoi. |
code_dept | Le département retenu, fourni ou déduit. |
received | Nombre d'éléments dans peis. |
added / updated | PEI créés / mis à jour. |
removed | PEI de votre source absents de cet envoi, retirés. |
skipped | PEI ignorés à la lecture. Le détail est dans reasons. |
unknown_types / unknown_etats | Les valeurs que je n'ai pas reconnues, pour corriger votre correspondance. |
Les raisons possibles
reasons | Ce qui s'est passé |
|---|---|
missing_ref | ref absente ou vide. PEI ignoré. |
bad_coords | lat ou lon absente, non numérique, hors bornes, ou les deux à 0. PEI ignoré. |
unknown_type | type absent ou hors liste (la valeur est dans unknown_types). PEI ignoré. |
not_object | Un élément de peis n'est pas un objet. Ignoré. |
upsert_error | Le PEI a été lu mais n'a pas pu être écrit en base. Il n'est compté ni dans added, ni dans updated, ni dans skipped. |
empty_no_delete | Aucun PEI valide : le garde-fou a joué, rien n'a été retiré. |
Un etat inconnu n'écarte pas le PEI : il est enregistré indisponible et la valeur apparaît dans unknown_etats.
Comment vos champs deviennent une couleur.
En intervention, le pompier doit lire en une seconde s'il peut engager un PEI. La couleur de la bordure vient de l'état, puis du débit (ou du volume pour les ressources statiques). L'emploi restreint est traité comme disponible pour la couleur, la restriction est affichée dans la fiche. Ce que voit le pompier, forme par forme et zoom par zoom, est détaillé sur la page Carte des points d'eau incendie (PEI).
Le bleu n'est pas un défaut. Un PEI disponible sans débit s'affiche en bleu, « disponible, débit non renseigné ». Pas en jaune : je ne veux pas faire croire à un débit faible quand il est seulement inconnu. Pour les calculs, le débit est alors estimé à la médiane.
Essayez
Poteau disponible à 72 m³/h : 60 m³/h ou plus.
Au zoom fort, un capuchon indique le réseau (NF S 62-200) : rouge sous pression permanente, bleu non pressurisé (citernes, points d'aspiration, PENA, colonnes), jaune quand la pression statique atteint 8 bar (à défaut, la pression dynamique).
Les codes que vous pouvez recevoir.
| Code | Message | Cause |
|---|---|---|
| 200 | Envoi traité. Lisez quand même skipped et reasons. | |
| 400 | Corps qui n'est pas en UTF-8, ou qui contient des caractères de contrôle. Refusé avant la lecture. | |
| 401 | X-Api-Key absente.X-Api-Key invalide ou inactive. | En-tête manquant, clé mal recopiée, ou clé désactivée. |
| 403 | Cette clé n'a pas le droit deci:import.Cette clé n'est rattachée à aucune entité. | La clé existe mais ne sert pas à ça. Écrivez-moi. |
| 422 | parse: corps JSON invalide | JSON mal formé, ou BOM en tête de fichier. |
| 422 | parse: clé "peis" (tableau) attendue | Ni objet avec peis, ni tableau à la racine. |
| 422 | Département indéterminé : fournis "code_dept" ou des "commune_code" INSEE. | Pas de code_dept et aucun PEI valide avec un code INSEE. |
Les erreurs 401, 403 et 422 ont toutes la même forme :
{
"status": 403,
"error": 403,
"messages": {
"error": "Cette clé n'a pas le droit deci:import."
}
}
Relire ce que l'application reçoit.
Ce sont les endpoints que l'application appelle. Ils sont publics, sans clé. Remplacez 26 par votre département.
curl "https://kilodelta.fr/api/v1/deci/manifest?depts=26"
{
"items": [
{
"dept": "26",
"count": 4807,
"updated_at": "2026-09-30 06:12:41",
"version": "3f9a1c2e"
}
]
}
count doit correspondre à added + updated de votre dernier envoi. version change
à chaque envoi : c'est elle qui dit aux téléphones de retélécharger.
curl "https://kilodelta.fr/api/v1/deci/positions?dept=26" // chaque ligne : [id KiloDelta, lat, lon, type_code, color_code, cap_code] { "v": "3f9a1c2e", "dept": "26", "peis": [ [418233, 44.933412, 4.892307, 0, 4, 0], … ] }
curl "https://kilodelta.fr/api/v1/deci/peis/418233"
La fiche renvoie tout ce que le pompier voit au tap. Votre ref y figure sous le nom
external_k, votre adresse sous street_address, avec etat_label,
type_label et le color_code calculé.
Ces réponses sont mises en cache (5 minutes pour manifest, plus longtemps pour les positions et les fiches,
avec un ETag). Un curl direct voit tout de suite la nouvelle version. La carte en haut de cette page
compte vos PEI dès le rechargement.
Ce qu'on me demande.
Nos PEI deviennent-ils publics ?
Comment retirer un PEI ?
Et si notre base n'a pas de débit ?
Pourquoi un PEI sans état est-il affiché indisponible ?
"etat": "disponible" pour les PEI que vous publiez : c'est vous qui
le dites, pas moi qui le suppose.
Nous avons plusieurs départements.
code_dept renseigné. La même clé sert pour tous : une
source est créée par département au premier envoi.
Notre SIG sait publier en WFS. Faut-il quand même utiliser l'API ?
Une question sur le format ? Écrivez-moi à nuno.castro@kilodelta.fr ou passez par le formulaire. Je réponds moi-même, et si un cas n'est pas prévu, j'adapte l'import.
Demander une clé