API Pluia public-api/1
Mêmes règles que l'écran Analyse et le rapport PDF de Pluia, mêmes droits et mêmes quotas que le compte titulaire de la clé. Chaque règle porte son statut (Vérifié, Texte officiel, Selon le projet, Sous conditions, Non déterminé, À vérifier, Pas de règle propre) et sa preuve (page, extrait, lien vers le PDF officiel à la page). Seules les règles is_verified = true ont été relues à la page citée du règlement officiel ; elles s'appliquent avec leur condition.
Démarrer
- Créez une clé dans Compte → Clés d’API (affichée une seule fois).
- Envoyez-la dans l’en-tête
Authorization: Bearer pluia_…, jamais dans l’adresse (une clé vue dans une adresse est révoquée). - Les opérations qui consomment un dossier, un export PDF ou une question décomptent le même quota que l’application.
curl -H "Authorization: Bearer $PLUIA_API_KEY" https://pluia.fr/api/v1/me curl -H "Authorization: Bearer $PLUIA_API_KEY" "https://pluia.fr/api/v1/parcels/search?address=1+place+de+la+mairie+91430+Igny"
Statuts des règles
Seules les valeurs « Vérifié » (is_verified = true) ont été relues à la page citée du PDF officiel ; elles s’appliquent avec leur condition. Toutes les autres se lisent dans le règlement, à la page indiquée par proof.href. Une source qui ne répond pas n’est jamais présentée comme « aucune règle ».
| assertion | Libellé | Sens |
|---|---|---|
verified | Vérifié | Valeur relue à la page citée du PDF officiel de la zone, à lire avec sa condition. |
official_text | Texte officiel | Passage officiel cité, valeur à confirmer en le lisant. |
by_project | Selon le projet | Valeur fixée par le règlement pour chaque cas du projet. |
conditional | Sous conditions | Valeur applicable sous conditions à lire. |
undetermined | Non déterminé | Règle non trouvée ou non tranchée dans les extraits : à lire dans le règlement. |
to_verify | À vérifier | Valeur introuvable à la page citée, ou preuve non relue dans le PDF officiel. |
zone_silent | Pas de règle propre | Le chapitre de la zone ne fixe pas de règle propre sur ce point (l’article qui l’encadre ne le vise pas) : à confirmer dans le règlement, jamais une valeur. |
Opérations
GET/
Point d'entrée de l'API
Sans clé.
GET/openapi.json
Contrat OpenAPI 3.1
Sans clé.
GET/docs
Documentation lisible
Sans clé.
GET/me
Ma clé, mon offre et mes quotas
Quotas lus comme l'écran Abonnement (mêmes chiffres).
Portée : read.
Réponse 200 :
{
"key": {
"public_id": "a1b2c3d4",
"name": "Cursor",
"environment": "live",
"scopes": [
"read",
"studio"
],
"expires_at": "2027-10-07T00:00:00Z",
"rate_limit_per_minute": 120,
"daily_consumption_cap": 20
},
"account": {
"plan": "pro",
"plan_label": "Pro",
"email_verified": true
},
"quotas": {
"dossiers": {
"used": 3,
"limit": 20,
"remaining": 17,
"unlimited": false
}
},
"period": {
"scope": "billing_period",
"start": "2026-10-01T00:00:00Z",
"end": "2026-10-31T23:59:59Z"
}
}GET/parcels/search
Trouver une parcelle
Exactement un moyen : address, idu, insee+section+number (+prefix), ou lat+lon. Un point hors de toute parcelle donne les parcelles les plus proches, jamais présentées comme certaines.
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
address | query | Adresse postale en France |
idu | query | IDU cadastral |
insee | query | Code INSEE (commune ou arrondissement) |
section | query | Section cadastrale |
number | query | Numéro de parcelle |
prefix | query | Préfixe (commune absorbée) |
lat | query | Latitude WGS84 |
lon | query | Longitude WGS84 |
limit | query | Nombre de candidats |
geometry | query | Ajouter la géométrie GeoJSON |
Réponse 200 :
{
"query": {
"idu": "91312000AE0290"
},
"candidates": [
{
"idu": "91312000AE0290",
"insee": "91312",
"commune": "Igny",
"prefix": "000",
"section": "AE",
"number": "0290",
"parcel_ref": "AE-0290",
"area_m2": 563,
"address": null,
"locality": "Igny",
"reference_point": {
"lat": 48.73697,
"lon": 2.22066
},
"match": "idu_exact",
"geocoder_score": null
}
],
"sources": [
{
"name": "API Carto – cadastre (IGN)",
"url": "https://apicarto.ign.fr/api/cadastre/parcelle"
}
],
"warnings": []
}GET/parcels/{idu}
Une parcelle avec sa géométrie
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
idu * | path | IDU cadastral, ex. 91312000AE0290 |
GET/parcels/{idu}/zoning
Zonage PLU d'une parcelle
Zone au point de référence (moyenne des sommets, point intérieur si elle tombe hors de la parcelle), zones qui traversent la parcelle avec leur part de surface, document du Géoportail, état de l'index et version du règlement indexé.
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
idu * | path | IDU cadastral, ex. 91312000AE0290 |
GET/parcels/{idu}/constraints
Servitudes, prescriptions et patrimoine
Une liste vaut « unavailable » quand sa source n'a pas répondu, « partial » quand une couche manque : jamais « aucune » par défaut.
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
idu * | path | IDU cadastral, ex. 91312000AE0290 |
GET/communes/{insee}/regulation
État du règlement d'une commune
Lecture seule : ne déclenche jamais d'indexation (la création d'un dossier le fait, comme dans l'application).
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
insee * | path | Code INSEE |
GET/documents/{document_id}
État d'un document indexé
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
document_id * | path | Document indexé (plu_documents) |
GET/documents/{document_id}/pages/{page}
Texte d'une page du règlement officiel
Documents cités par un de vos dossiers (ou téléversés par vous), 300 pages par jour. Texte de la couche texte du PDF officiel, non retouché ; null si le PDF est scanné ou pas encore extrait (ouvrir pdf_url).
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
document_id * | path | Document indexé (plu_documents) |
page * | path | Page (1 = première) |
GET/dossiers
Lister mes dossiers
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
idu | query | Dossier d'une parcelle |
limit | query | Taille de page |
offset | query | Décalage |
POST/dossiers
Créer le dossier PLU d'une parcelle (1 dossier)
Même analyse que l'application (même moteur, même quota, réservation idempotente par parcelle et période). Commune non indexée : 202 indexing, rien décompté. Commune sans PLU (RNU, carte communale, POS) : dossier enregistré, non décompté.
Portée : analyze. Quota : dossiers (même décompte que l’application).
| Paramètre | Où | Description |
|---|---|---|
Idempotency-Key | header | Même clé + même corps = même opération, jamais décomptée deux fois ; autre corps = 409. |
Prefer | header | Attente maximale (RFC 7240, 1 à 240 s, défaut 60) ; au-delà, 202 processing puis relance à l'identique. |
Corps :
{
"idu": "91312000AE0290"
}GET/dossiers/{id}
Un dossier
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
GET/dossiers/{id}/rules
Règles clés prouvées
Mêmes valeurs, statuts, pages et liens que l'écran Analyse et le rapport PDF. Chaque règle : assertion (statut), is_verified, proof (page, extrait, lien PDF#page=N).
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
include | query | topics (12 thèmes) et/ou attention (points d'attention) |
Réponse 200 :
{
"dossier_id": "0d8f3c1e-0000-4000-8000-000000000001",
"zone": "UH",
"document": {
"name": "91312_PLU_20231005",
"approval": "05/10/2023",
"version_status": "current"
},
"versioned": true,
"key_rules": [
{
"id": "emprise_sol",
"label": "Emprise au sol",
"state": "value",
"assertion": "verified",
"assertion_label": "Vérifié",
"is_verified": true,
"value": "40 %",
"measure": {
"value": 40,
"unit": "%"
},
"condition": "",
"to_read": "",
"reference": {
"article": "Article UH 9",
"pages": "p. 87",
"label": "Article UH 9 · p. 87",
"href": "https://data.geopf.fr/annexes/gpu/documents/DU_91312/…/91312_reglement_20231005.pdf#page=87"
},
"proof": {
"status": "verified",
"label": "Vérifié · p. 87",
"page": 87,
"excerpt": "L'emprise au sol des constructions ne peut excéder 40 %…",
"href": "https://data.geopf.fr/…#page=87",
"figures": [
"40 %"
],
"official": true
}
}
],
"notice": "Seules les valeurs « Vérifié » (is_verified = true) ont été relues à la page citée du règlement officiel ; elles s’appliquent avec leur condition. Les autres se lisent dans le règlement à la page indiquée.",
"sources": []
}GET/dossiers/{id}/regulation-version
Version du règlement servi
Portée : read.
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
GET/dossiers/{id}/studio-rules
Seuils sourcés pour le Studio et la CAO (studio-rules/1)
Portée : studio.
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
If-None-Match | header | ETag d'une réponse précédente |
POST/dossiers/{id}/checks
Vérifier un projet (verdict règle par règle)
Verdict d’un projet (extension, véranda, piscine, garage, abri…) sur les règles du dossier, avec le moteur du Studio : mêmes seuils, mêmes textes que le Studio, l’écran et le PDF. Les mesures sont DÉCLARÉES par l’appelant (aucun plan n’est contrôlé) et le disent dans `warnings`. Une mesure absente reste « Non déterminé » ; sans `existing_footprint_m2`, l’emprise n’est jamais déclarée conforme. Le code de sortie du CLI suit `exit_code` (0 oui, 1 non, 2 sous conditions ou non déterminé). Aucun quota, aucun appel LLM. Même garde d’offre que le Studio ; 503 tant que le moteur n’est pas installé sur le serveur.
Portée : studio.
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
Corps :
{
"project_type": "extension",
"measures": {
"height_m": 3.5,
"project_footprint_m2": 30,
"existing_footprint_m2": 120,
"street_distance_m": 6,
"side_distance_m": 4,
"rear_distance_m": 10,
"open_ground_m2": 250
},
"answers": {
"facade_openings": false
}
}Réponse 200 :
{
"contract": "pluia-check/1",
"engine": "rule-eval/1",
"rules_contract": "studio-rules/1",
"dossier_id": "0d8f3c1e-5a2b-4c3d-8e9f-0a1b2c3d4e5f",
"verdict": {
"code": "conditional",
"label": "Sous conditions",
"subtitle": null,
"detail": "Le verdict dépend de vos précisions sur le projet.",
"conclusive": false,
"reasons": [],
"corrective_actions": []
},
"exit_code": 2,
"checks": [
{
"id": "hauteur",
"family": "hauteur",
"metric": "height_max",
"label": "Hauteur maximale",
"status": "pass",
"status_label": "Conforme",
"requirement": "Maximum 10 m",
"measured": "3,5 m",
"note": null,
"confidence": "high",
"question_ids": [],
"corrective_action": null,
"indicative": false,
"source": {
"article": "Article UC 10",
"article_verified": true,
"page": 88,
"excerpt": "La hauteur maximale des constructions ne peut excéder 10 mètres.",
"href": "https://www.geoportail-urbanisme.gouv.fr/api/document/0123/files/reglement.pdf#page=88",
"document_id": "6b1e0a52-3f4d-4c5b-9a6e-7f8091a2b3c4",
"assertion": "official_text"
}
}
],
"questions": [
{
"id": "facade_openings",
"label": "La façade du projet face à la limite aura-t-elle des fenêtres ou des baies ?",
"kind": "boolean",
"decisive": true,
"options": [
{
"value": true,
"label": "Oui"
},
{
"value": false,
"label": "Non"
}
],
"answer": false
}
],
"blockers": [
"Retrait des limites séparatives : répondre à la question sur les baies"
],
"source_status": "ok",
"warnings": [
"Mesures déclarées par l’appelant, non contrôlées sur un plan."
]
}POST/dossiers/{id}/report
Rapport PDF du dossier (1 export)
Le rapport de l'application (même modèle que l'écran). Rejeu de la même Idempotency-Key : jamais décompté deux fois. Rendu PDF indisponible : HTML imprimable (Pluia-Report-Format: html).
Portée : report. Quota : pdf_exports (même décompte que l’application).
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
Idempotency-Key | header | Même clé + même corps = même opération, jamais décomptée deux fois ; autre corps = 409. |
Prefer | header | Attente maximale (RFC 7240, 1 à 240 s, défaut 60) ; au-delà, 202 processing puis relance à l'identique. |
Corps :
[]
POST/dossiers/{id}/questions
Poser une question sur le règlement (1 question)
Agent PLU de l'application (même moteur de réponse, mêmes citations). Chaque question compte ; un refus motivé est une réponse valide.
Portée : ask. Quota : agent_questions (même décompte que l’application).
| Paramètre | Où | Description |
|---|---|---|
id * | path | Dossier (parcel_analyses) |
Idempotency-Key | header | Même clé + même corps = même opération, jamais décomptée deux fois ; autre corps = 409. |
Corps :
{
"question": "Puis-je construire en limite séparative ?"
}Agents IA (MCP) et CLI
Le même noyau est exposé au protocole MCP : POST https://pluia.fr/mcp (Streamable HTTP, JSON-RPC, Authorization: Bearer). Les outils (trouver_parcelle, zonage_parcelle, regles_parcelle, verifier_projet, citer_reglement…) exécutent les opérations de cette page avec la même clé, les mêmes portées et les mêmes quotas ; un client ne voit que les outils que les portées de sa clé ouvrent, et ceux qui consomment demandent l’accord de l’utilisateur.
claude mcp add --transport http pluia https://pluia.fr/mcp --header "Authorization: Bearer $PLUIA_API_KEY"
La CLI pluia-plu (login, regles, verifier, surveiller…) et son pont stdio pluia-plu mcp sont dans le dépôt, packages/pluia-plu.
Erreurs
Format application/problem+json (RFC 9457) : type, title, status, code, detail, request_id, éventuellement legacy_code et details.
| code | HTTP | Titre |
|---|---|---|
invalid_api_key | 401 | Clé d’API invalide |
api_key_in_url | 400 | Clé d’API dans l’adresse |
bad_request | 400 | Requête invalide |
scope_missing | 403 | Portée manquante |
email_verification_required | 403 | E-mail à vérifier |
plan_required | 403 | Offre requise |
current_password_invalid | 403 | Mot de passe incorrect |
quota_exceeded | 402 | Quota épuisé |
free_parcel_locked | 402 | Découverte liée à une autre parcelle |
not_found | 404 | Ressource introuvable |
parcel_not_found | 404 | Parcelle introuvable |
dossier_required | 404 | Dossier à créer |
method_not_allowed | 405 | Méthode non autorisée |
not_acceptable | 406 | Format de réponse non disponible |
idempotency_conflict | 409 | Clé d’idempotence déjà utilisée |
operation_in_progress | 409 | Opération en cours |
operation_already_completed | 409 | Opération déjà traitée |
manual_review_required | 409 | Vérification nécessaire |
key_limit_reached | 409 | Nombre maximal de clés atteint |
payload_too_large | 413 | Corps trop volumineux |
unsupported_media_type | 415 | Format de corps non pris en charge |
validation_failed | 422 | Paramètres invalides |
rate_limited | 429 | Trop de requêtes |
too_many_concurrent | 429 | Trop d’opérations simultanées |
consumption_cap_reached | 429 | Plafond quotidien de la clé atteint |
global_cap_reached | 429 | Capacité quotidienne du service atteinte |
indexing_budget_exhausted | 429 | Plafond d’indexations atteint |
internal_error | 500 | Erreur interne |
upstream_unavailable | 502 | Source officielle indisponible |
service_unavailable | 503 | Service indisponible |
timeout | 504 | Délai dépassé |