{
  "openapi": "3.0.3",
  "info": {
    "title": "API Capfile Pro",
    "version": "1.1",
    "description": "Accès aux **données brutes** de soutirage (consommation) et d'injection (production) des compteurs : informations techniques, index quotidiens, courbe de charge, et **notifications par webhook** à l'arrivée de nouvelles données. Aucune information tarifaire n'est renvoyée.\n\nToutes les réponses sont en JSON et partagent la même enveloppe `{ success, code, message, data }`. En cas d'erreur, le statut HTTP reprend la valeur de `code`.\n\nDocumentation détaillée et guide d'intégration : [/pro/v1.1/doc/](./) — démo interactive : [/pro/v1.1/demo/](../demo/).",
    "contact": {
      "name": "Capfile",
      "url": "https://www.capfile.com"
    }
  },
  "servers": [
    {
      "url": "https://api.capfile.com/pro/v1.1"
    }
  ],
  "tags": [
    {
      "name": "Clé API",
      "description": "Obtention et renouvellement de la clé API (Basic Auth)."
    },
    {
      "name": "Compteurs",
      "description": "Compteurs accessibles avec la clé API."
    },
    {
      "name": "Données",
      "description": "Données brutes d'un compteur : infos techniques, index, courbe de charge."
    },
    {
      "name": "Quotas",
      "description": "Limites d'appels par jour, semaine et mois calendaires."
    },
    {
      "name": "Webhook",
      "description": "Notification de l'arrivée de nouvelles données (POST JSON signé HMAC-SHA256)."
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BasicAuth": []
    }
  ],
  "paths": {
    "/api_key": {
      "get": {
        "tags": [
          "Clé API"
        ],
        "summary": "Clé API du compte",
        "description": "Renvoie la clé API active du compte (créée automatiquement si elle n'existe pas encore). Basic Auth avec l'identifiant (ou l'e-mail) et le mot de passe Capfile.",
        "security": [
          {
            "BasicAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Clé API",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ApiKey"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Clé API",
                  "data": {
                    "api_key": "TamxBsK9hTjszezcSy4sdfsdfsdfqRmYUFo"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api_key/renew": {
      "get": {
        "tags": [
          "Clé API"
        ],
        "summary": "Renouvelle la clé API",
        "description": "Révoque la clé actuelle et en génère une nouvelle immédiatement.",
        "security": [
          {
            "BasicAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Nouvelle clé API",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/ApiKey"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Clé API renouvelée",
                  "data": {
                    "api_key": "anQBz1brst89eymswVCqhk29mpKqnb7n"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/meters": {
      "get": {
        "tags": [
          "Compteurs"
        ],
        "summary": "Compteurs accessibles",
        "description": "Vos compteurs actifs, plus ceux partagés avec vous par d'autres comptes Capfile (`shared: true`) lorsque le partage donne accès aux infos, aux index et à la courbe de charge.",
        "responses": {
          "200": {
            "description": "Liste des compteurs",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Meters"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Liste des compteurs",
                  "data": [
                    {
                      "prm": "12345678901234",
                      "name": "Maison principale"
                    },
                    {
                      "prm": "98765432109876",
                      "name": "Appartement voisin",
                      "shared": true
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/data/{prm}/info": {
      "get": {
        "tags": [
          "Données"
        ],
        "summary": "Informations techniques d'un compteur",
        "description": "Segments contractuels, informations techniques et contractuelles (ITC) et grandeurs physiques disponibles par flux. Les blocs `soutirage` / `injection` sont absents si le compteur ne gère pas ce flux. **C'est ici que l'on découvre les codes `{gp}` utilisables avec `/index`.**",
        "parameters": [
          {
            "$ref": "#/components/parameters/prm"
          }
        ],
        "responses": {
          "200": {
            "description": "Infos du compteur",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/MeterInfo"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "OK",
                  "data": {
                    "prm": "12345678901234",
                    "name": "Maison principale",
                    "segments": {
                      "C5": {
                        "segment": "C5",
                        "etatContractuel": "En service",
                        "etatContractuelCode": "SERVC"
                      }
                    },
                    "itc": {
                      "point": {
                        "donneesGenerales": {
                          "...": "..."
                        }
                      }
                    },
                    "soutirage": {
                      "grandeurs_physiques": {
                        "EA": "Energie Active (Wh)"
                      },
                      "itc": {
                        "...": "..."
                      }
                    },
                    "injection": {
                      "grandeurs_physiques": {
                        "EA": "Energie Active (Wh)"
                      },
                      "itc": {
                        "...": "..."
                      },
                      "puissance_raccordement": 12.0
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/data/{prm}/index/{sens}/{gp}": {
      "get": {
        "tags": [
          "Données"
        ],
        "summary": "Historique d'index quotidiens",
        "description": "Index relevés chaque jour à minuit pour une grandeur physique. Chaque ligne contient, par cadran, la **différence avec la veille** (`NOM_CADRAN`) et l'**index cumulé** (`cadran_NOM_CADRAN`), plus `Total`. Fenêtre maximale : 3 ans. Par défaut : les 7 derniers jours.",
        "parameters": [
          {
            "$ref": "#/components/parameters/prm"
          },
          {
            "$ref": "#/components/parameters/sens"
          },
          {
            "name": "gp",
            "in": "path",
            "required": true,
            "description": "Grandeur physique : code renvoyé dans `grandeurs_physiques` par `/info` pour ce compteur et ce flux (EA, ER, ERI, ERC, PMA, TF, DD, DE, DQ…).",
            "schema": {
              "type": "string",
              "example": "EA"
            }
          },
          {
            "$ref": "#/components/parameters/start_date"
          },
          {
            "$ref": "#/components/parameters/end_date"
          }
        ],
        "responses": {
          "200": {
            "description": "Index",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/IndexData"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "OK",
                  "data": {
                    "head": {
                      "prm": "12345678901234",
                      "type": "index",
                      "sens": "sou",
                      "grandeur_physique": "EA",
                      "start_date": "2026-03-04",
                      "end_date": "2026-03-11"
                    },
                    "data": [
                      {
                        "date": "2026-03-04",
                        "BASE": "11593",
                        "Total": "11593",
                        "cadran_BASE": "17539897",
                        "cadran_Total": "17539897"
                      },
                      {
                        "date": "2026-03-05",
                        "BASE": "11021",
                        "Total": "11021",
                        "cadran_BASE": "17550918",
                        "cadran_Total": "17550918"
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/data/{prm}/loadcurve/{sens}": {
      "get": {
        "tags": [
          "Données"
        ],
        "summary": "Courbe de charge",
        "description": "Énergie active (Wh) au pas du compteur (`p` en minutes : 5, 10, 15, 30 ou 60), points triés par date croissante. Fenêtre maximale : 1 an. Par défaut : les 7 derniers jours. Les points sont horodatés à la **fin** de leur intervalle, en heure légale française avec décalage explicite (`+01:00` / `+02:00`) ; `start_date` et `end_date` sont des jours civils français. Dernier dimanche de mars : 23 h de données (pas de point 02:xx) ; dernier dimanche d'octobre : 24 h de données, les points 02:xx étant ceux de l'heure d'hiver.",
        "parameters": [
          {
            "$ref": "#/components/parameters/prm"
          },
          {
            "$ref": "#/components/parameters/sens"
          },
          {
            "$ref": "#/components/parameters/start_date"
          },
          {
            "$ref": "#/components/parameters/end_date"
          }
        ],
        "responses": {
          "200": {
            "description": "Courbe de charge",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/LoadcurveData"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "OK",
                  "data": {
                    "head": {
                      "prm": "12345678901234",
                      "type": "loadcurve",
                      "sens": "sou",
                      "start_date": "2026-03-01",
                      "end_date": "2026-03-11",
                      "timezone": "Europe/Paris"
                    },
                    "data": [
                      {
                        "date_time": "2026-03-01T00:30:00+01:00",
                        "p": 30,
                        "Wh": 81
                      },
                      {
                        "date_time": "2026-03-01T01:00:00+01:00",
                        "p": 30,
                        "Wh": 92
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/quotas": {
      "get": {
        "tags": [
          "Quotas"
        ],
        "summary": "Usage et quotas",
        "description": "Quotas autorisés, consommation en cours et appels restants par période calendaire (jour, semaine du lundi au dimanche, mois).",
        "responses": {
          "200": {
            "description": "Usage et quotas",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Quotas"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Usage et quotas",
                  "data": {
                    "quotas": {
                      "day": 5000,
                      "week": 50000,
                      "month": 300000
                    },
                    "usage": {
                      "day": 120,
                      "week": 890,
                      "month": 3400
                    },
                    "remaining": {
                      "day": 4880,
                      "week": 49110,
                      "month": 296600
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhook": {
      "get": {
        "tags": [
          "Webhook"
        ],
        "summary": "Configuration du webhook",
        "responses": {
          "200": {
            "description": "Configuration",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookConfig"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Configuration du webhook",
                  "data": {
                    "url": "https://www.monsite.fr/capfile/webhook",
                    "secret": "3f9c0a…",
                    "ssl_verify_peer": true,
                    "ssl_verify_host": true,
                    "configured": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Webhook"
        ],
        "summary": "Enregistre ou modifie la configuration",
        "description": "Les champs absents conservent leur valeur. `url` est obligatoire à la première configuration : absolue, en http(s), vers un hôte public (les adresses locales ou privées sont refusées). `secret` : 16 caractères minimum ; s'il est omis à la première configuration, un secret aléatoire est généré et renvoyé. `PUT /webhook` est un alias.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConfigInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Configuration enregistrée",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookConfig"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Configuration du webhook enregistrée",
                  "data": {
                    "url": "https://www.monsite.fr/capfile/webhook",
                    "secret": "3f9c0a…",
                    "ssl_verify_peer": true,
                    "ssl_verify_host": true,
                    "configured": true
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "callbacks": {
          "dataUpdated": {
            "{$request.body#/url}": {
              "post": {
                "summary": "Notification envoyée par Capfile à votre URL",
                "description": "POST JSON à chaque arrivée de données pour un compteur listé par `/meters`. En-tête `X-Signature` = HMAC-SHA256 hexadécimal du corps brut, calculé avec votre `secret` — à vérifier avant tout traitement. Délais : 5 s de connexion, 10 s au total ; répondez en 2xx. Pas de nouvelle tentative en cas d'échec. `start_date`/`end_date` absents pour `type: info`.",
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/WebhookNotification"
                      }
                    }
                  }
                },
                "responses": {
                  "2XX": {
                    "description": "Notification acceptée"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Webhook"
        ],
        "summary": "Alias de POST /webhook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConfigInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Configuration enregistrée",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookConfig"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhook"
        ],
        "summary": "Supprime la configuration",
        "description": "Arrête les notifications. `POST /webhook/delete` est un alias pour les clients HTTP sans méthode DELETE.",
        "responses": {
          "200": {
            "description": "Configuration supprimée",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookConfig"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Configuration du webhook supprimée",
                  "data": {
                    "url": "",
                    "secret": "",
                    "ssl_verify_peer": true,
                    "ssl_verify_host": true,
                    "configured": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhook/delete": {
      "post": {
        "tags": [
          "Webhook"
        ],
        "summary": "Alias de DELETE /webhook",
        "responses": {
          "200": {
            "description": "Configuration supprimée",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookConfig"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhook/test": {
      "post": {
        "tags": [
          "Webhook"
        ],
        "summary": "Provoque un appel de test",
        "description": "Capfile appelle immédiatement votre URL avec une notification d'exemple identique à une notification réelle, plus le champ `\"test\": true`. La réponse décrit le résultat de cet appel. Corps optionnel.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookTestInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Appel effectué, votre serveur a répondu en 2xx",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookTestResult"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "code": 200,
                  "message": "Webhook appelé",
                  "data": {
                    "status": "OK",
                    "http_code": 200,
                    "duration_ms": 143,
                    "response": "{\"received\":true}",
                    "payload": {
                      "hook": "data_updated",
                      "prm": "12345678901234",
                      "name": "Bureau",
                      "type": "loadcurve",
                      "start_date": "2026-10-01",
                      "end_date": "2026-10-07",
                      "test": true
                    },
                    "signature": "a3c1…"
                  }
                }
              }
            }
          },
          "502": {
            "description": "Votre serveur n'a pas répondu en 2xx (ou pas du tout) : `status` et `response` donnent le détail",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WebhookTestResult"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": false,
                  "code": 502,
                  "message": "Échec de l'appel du webhook",
                  "data": {
                    "status": "Erreur HTTP 500",
                    "http_code": 500,
                    "duration_ms": 87,
                    "response": "Internal Server Error",
                    "payload": {
                      "hook": "data_updated",
                      "prm": "12345678901234",
                      "name": "Bureau",
                      "type": "loadcurve",
                      "start_date": "2026-10-01",
                      "end_date": "2026-10-07",
                      "test": true
                    },
                    "signature": "a3c1…"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Clé API du compte (obtenue via `GET /api_key`). Mode recommandé pour l'accès aux données."
      },
      "BasicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "Identifiant (ou e-mail) et mot de passe Capfile. Requis pour `/api_key`, accepté partout ailleurs."
      }
    },
    "parameters": {
      "prm": {
        "name": "prm",
        "in": "path",
        "required": true,
        "description": "Numéro de PRM du compteur (14 chiffres)",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]{14}$",
          "example": "12345678901234"
        }
      },
      "sens": {
        "name": "sens",
        "in": "path",
        "required": true,
        "description": "Flux : `sou` = soutirage (consommation), `inj` = injection (production)",
        "schema": {
          "type": "string",
          "enum": [
            "sou",
            "inj"
          ]
        }
      },
      "start_date": {
        "name": "start_date",
        "in": "query",
        "required": false,
        "description": "Date de début (YYYY-MM-DD). Défaut : il y a 7 jours.",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-10-01"
        }
      },
      "end_date": {
        "name": "end_date",
        "in": "query",
        "required": false,
        "description": "Date de fin (YYYY-MM-DD). Défaut : aujourd'hui.",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-10-07"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Requête invalide : paramètre manquant ou invalide (PRM, sens, grandeur physique, dates…)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "success": false,
              "code": 400,
              "message": "Paramètre invalide"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Identifiants Basic Auth incorrects ou clé API non reconnue",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "success": false,
              "code": 401,
              "message": "Clé API invalide"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Compte non pro, compteur non accessible depuis votre compte, ou période demandée au-delà de l'historique autorisé (3 ans pour `/index`, 1 an pour `/loadcurve`)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "success": false,
              "code": 403,
              "message": "Accès refusé."
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Un quota (jour, semaine ou mois) est atteint",
        "headers": {
          "Retry-After": {
            "description": "Secondes à attendre avant le prochain appel possible",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "code": {
                  "type": "integer",
                  "example": 429
                },
                "message": {
                  "type": "string"
                },
                "retry_after_seconds": {
                  "type": "integer"
                },
                "next_retry": {
                  "type": "string",
                  "example": "tomorrow"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "success",
          "code"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "code": {
            "type": "integer",
            "description": "Code applicatif ; égal au statut HTTP en cas d'erreur"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "api_key": {
            "type": "string"
          }
        }
      },
      "Meter": {
        "type": "object",
        "required": [
          "prm",
          "name"
        ],
        "properties": {
          "prm": {
            "type": "string",
            "example": "12345678901234"
          },
          "name": {
            "type": "string",
            "example": "Maison principale"
          },
          "shared": {
            "type": "boolean",
            "description": "Présent et à true uniquement pour un compteur partagé par un autre compte"
          }
        }
      },
      "Meters": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Meter"
        }
      },
      "FluxInfo": {
        "type": "object",
        "properties": {
          "grandeurs_physiques": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Dictionnaire code → libellé des grandeurs physiques disponibles pour ce flux",
            "example": {
              "EA": "Energie Active (Wh)"
            }
          },
          "itc": {
            "type": "object",
            "nullable": true,
            "description": "Informations techniques spécifiques au flux (structure variable)"
          },
          "puissance_raccordement": {
            "type": "number",
            "description": "Injection uniquement : puissance de raccordement en kVA, 0 si inconnue"
          }
        }
      },
      "MeterInfo": {
        "type": "object",
        "properties": {
          "prm": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "segments": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "segment": {
                  "type": "string"
                },
                "etatContractuel": {
                  "type": "string"
                },
                "etatContractuelCode": {
                  "type": "string"
                }
              }
            }
          },
          "itc": {
            "type": "object",
            "description": "Informations techniques et contractuelles du point (structure riche et variable)"
          },
          "soutirage": {
            "$ref": "#/components/schemas/FluxInfo"
          },
          "injection": {
            "$ref": "#/components/schemas/FluxInfo"
          }
        }
      },
      "IndexHead": {
        "type": "object",
        "properties": {
          "prm": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "index"
            ]
          },
          "sens": {
            "type": "string",
            "enum": [
              "sou",
              "inj"
            ],
            "description": "Flux demandé, tel que passé dans l'appel : sou = soutirage, inj = injection"
          },
          "grandeur_physique": {
            "type": "string"
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          }
        }
      },
      "IndexRow": {
        "type": "object",
        "description": "Une ligne par jour civil, même convention pour tous les compteurs : la ligne datée D porte l'index relevé à la fin de la journée D (`cadran_NOM_CADRAN`, `cadran_Total`) et l'énergie de la journée D (`NOM_CADRAN` = différence avec la veille, `Total`). Un seul relevé par jour est retenu, le premier après minuit. Les noms de cadrans dépendent du compteur. Valeurs renvoyées sous forme de chaînes.",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "IndexData": {
        "type": "object",
        "properties": {
          "head": {
            "$ref": "#/components/schemas/IndexHead"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IndexRow"
            }
          }
        }
      },
      "LoadcurveHead": {
        "type": "object",
        "properties": {
          "prm": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "loadcurve"
            ]
          },
          "sens": {
            "type": "string",
            "enum": [
              "sou",
              "inj"
            ],
            "description": "Flux demandé, tel que passé dans l'appel : sou = soutirage, inj = injection"
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          },
          "timezone": {
            "type": "string",
            "enum": [
              "Europe/Paris"
            ],
            "description": "Fuseau des décalages de date_time et des jours start_date / end_date"
          }
        }
      },
      "LoadcurvePoint": {
        "type": "object",
        "properties": {
          "date_time": {
            "type": "string",
            "format": "date-time",
            "description": "Fin de l'intervalle de mesure (convention Enedis), ISO 8601 en heure légale française avec décalage explicite : +01:00 en hiver, +02:00 en été",
            "example": "2026-03-01T00:30:00+01:00"
          },
          "p": {
            "type": "integer",
            "description": "Pas de mesure en minutes, porté par chaque point : il peut changer au cours de l'historique d'un même compteur (5, 10, 15, 30 ou 60)",
            "example": 30
          },
          "Wh": {
            "type": "number",
            "description": "Énergie active sur l'intervalle, en Wh",
            "example": 92
          }
        }
      },
      "LoadcurveData": {
        "type": "object",
        "properties": {
          "head": {
            "$ref": "#/components/schemas/LoadcurveHead"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadcurvePoint"
            }
          }
        }
      },
      "QuotaPeriods": {
        "type": "object",
        "properties": {
          "day": {
            "type": "integer"
          },
          "week": {
            "type": "integer"
          },
          "month": {
            "type": "integer"
          }
        }
      },
      "Quotas": {
        "type": "object",
        "properties": {
          "quotas": {
            "$ref": "#/components/schemas/QuotaPeriods"
          },
          "usage": {
            "$ref": "#/components/schemas/QuotaPeriods"
          },
          "remaining": {
            "$ref": "#/components/schemas/QuotaPeriods"
          }
        }
      },
      "WebhookConfig": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "secret": {
            "type": "string"
          },
          "ssl_verify_peer": {
            "type": "boolean"
          },
          "ssl_verify_host": {
            "type": "boolean"
          },
          "configured": {
            "type": "boolean",
            "description": "true quand url et secret sont renseignés : notifications actives"
          }
        }
      },
      "WebhookConfigInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://www.monsite.fr/capfile/webhook"
          },
          "secret": {
            "type": "string",
            "minLength": 16,
            "description": "Omis : généré à la première configuration, conservé ensuite"
          },
          "ssl_verify_peer": {
            "type": "boolean",
            "default": true
          },
          "ssl_verify_host": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WebhookTestInput": {
        "type": "object",
        "properties": {
          "prm": {
            "type": "string",
            "description": "Défaut : votre premier compteur",
            "example": "12345678901234"
          },
          "type": {
            "type": "string",
            "enum": [
              "info",
              "index",
              "loadcurve"
            ],
            "default": "loadcurve"
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-01"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-07"
          }
        }
      },
      "WebhookNotification": {
        "type": "object",
        "required": [
          "hook",
          "prm",
          "name",
          "type"
        ],
        "properties": {
          "hook": {
            "type": "string",
            "enum": [
              "data_updated"
            ]
          },
          "prm": {
            "type": "string",
            "example": "12345678901234"
          },
          "name": {
            "type": "string",
            "example": "Bureau"
          },
          "type": {
            "type": "string",
            "enum": [
              "info",
              "index",
              "loadcurve"
            ],
            "description": "Nature des données arrivées ; le flux (soutirage/injection) n'est pas précisé"
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Absent pour type = info"
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Absent pour type = info"
          },
          "test": {
            "type": "boolean",
            "description": "Présent et à true uniquement pour un appel déclenché par /webhook/test"
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "OK, « Erreur HTTP n » ou « Erreur cURL : … »"
          },
          "http_code": {
            "type": "integer"
          },
          "duration_ms": {
            "type": "integer"
          },
          "response": {
            "type": "string",
            "description": "Corps renvoyé par votre serveur (2000 premiers caractères)"
          },
          "payload": {
            "$ref": "#/components/schemas/WebhookNotification"
          },
          "signature": {
            "type": "string",
            "description": "Valeur envoyée dans X-Signature"
          }
        }
      }
    }
  }
}