{
  "components": {
    "parameters": {
      "key": {
        "description": "Clé privée d'un compte admin ou modérateur",
        "in": "query",
        "name": "key",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "keyOptional": {
        "description": "Clé privée d'un compte admin ou modérateur",
        "in": "query",
        "name": "key",
        "schema": {
          "type": "string"
        }
      },
      "scope": {
        "description": "Identifiant du territoire (voir la page de chaque ville)",
        "example": "34_montpellier",
        "in": "query",
        "name": "scope",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "scopeOptional": {
        "description": "Identifiant du territoire ; sans scope, toutes les observations de l'instance",
        "example": "34_montpellier",
        "in": "query",
        "name": "scope",
        "schema": {
          "type": "string"
        }
      },
      "secretidOptional": {
        "description": "Clé secrète de l'observation, retournée à sa création",
        "in": "query",
        "name": "secretid",
        "schema": {
          "type": "string"
        }
      },
      "token": {
        "description": "Token de l'observation",
        "in": "query",
        "name": "token",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Error": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Erreur"
      },
      "Status": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "status": {
                  "description": "Toujours 0 (conservé pour les anciennes applications : le code HTTP fait foi)",
                  "example": 0,
                  "type": "integer"
                }
              },
              "type": "object"
            }
          }
        },
        "description": "OK"
      }
    },
    "schemas": {
      "Category": {
        "properties": {
          "catcolor": {
            "description": "Nom CSS ou #rrggbb",
            "type": "string"
          },
          "catcustom": {
            "description": "Catégorie propre à l'instance (facultatif ; backend ≥ 0.0.23)",
            "type": "boolean"
          },
          "catdisable": {
            "description": "Catégorie à ne plus proposer (facultatif)",
            "type": "boolean"
          },
          "catid": {
            "type": "integer"
          },
          "catname": {
            "type": "string"
          },
          "catname_en_US": {
            "description": "Nom en anglais (facultatif)",
            "type": "string"
          },
          "catresolvable": {
            "description": "Les citoyens peuvent déclarer l'observation résolue",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "City": {
        "properties": {
          "area": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "population": {
            "type": "string"
          },
          "postcode": {
            "type": "string"
          },
          "website": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "Error": {
        "properties": {
          "error": {
            "properties": {
              "code": {
                "example": "SCOPENOTEXIST",
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "status": {
                "example": 404,
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "Issue": {
        "properties": {
          "address": {
            "type": "string"
          },
          "approved": {
            "description": "0 : à modérer ; 1 : approuvée ; 2 : refusée",
            "type": "string"
          },
          "archived": {
            "description": "1 : archivée, 0 sinon (uniquement avec `archived=1`, backend ≥ 0.0.29)",
            "type": "integer"
          },
          "categorie": {
            "description": "Identifiant de catégorie",
            "type": "string"
          },
          "cityname": {
            "description": "Nom de la ville, absent si inconnu (backend ≥ 0.0.13)",
            "type": "string"
          },
          "comment": {
            "type": "string"
          },
          "coordinates_lat": {
            "type": "string"
          },
          "coordinates_lon": {
            "type": "string"
          },
          "distance": {
            "description": "Distance en mètres au point `lat`/`lon` (uniquement avec `lat`/`lon`/`radius`)",
            "type": "number"
          },
          "explanation": {
            "type": "string"
          },
          "group": {
            "deprecated": true,
            "type": "integer"
          },
          "status": {
            "description": "0 : nouvelle ; 1 : résolue ; 2 : prise en compte ; 3 : en cours de résolution ; 4 : indiquée comme résolue (backend ≥ 0.0.10)",
            "type": "integer"
          },
          "time": {
            "description": "Timestamp Unix (secondes)",
            "type": "string"
          },
          "token": {
            "example": "52299F01",
            "type": "string"
          }
        },
        "type": "object"
      },
      "IssueFeatureCollection": {
        "description": "Réponse avec `format=geojson`",
        "properties": {
          "features": {
            "items": {
              "properties": {
                "geometry": {
                  "properties": {
                    "coordinates": {
                      "items": {
                        "type": "number"
                      },
                      "type": "array"
                    },
                    "type": {
                      "example": "Point",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "properties": {
                  "type": "object"
                },
                "type": {
                  "example": "Feature",
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "type": {
            "example": "FeatureCollection",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Scope": {
        "properties": {
          "association_url": {
            "description": "Site web de l'association qui gère le territoire, vide s'il n'est pas renseigné (\u003e= 0.0.28)",
            "type": "string"
          },
          "backend_version": {
            "type": "string"
          },
          "cities": {
            "items": {
              "$ref": "#/components/schemas/City"
            },
            "type": "array"
          },
          "contact_email": {
            "type": "string"
          },
          "coordinate_lat_max": {
            "type": "string"
          },
          "coordinate_lat_min": {
            "type": "string"
          },
          "coordinate_lon_max": {
            "type": "string"
          },
          "coordinate_lon_min": {
            "type": "string"
          },
          "department": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "map_center_string": {
            "description": "\"latitude,longitude\" du centre de la carte",
            "type": "string"
          },
          "map_url": {
            "type": "string"
          },
          "map_zoom": {
            "type": "string"
          },
          "nominatim_urlbase": {
            "description": "Backend ≥ 0.0.14",
            "type": "string"
          },
          "tweet_content": {
            "description": "Valeur existante, plus modifiable depuis le backend 0.0.23",
            "type": "string"
          },
          "twitter": {
            "description": "Valeur existante, plus modifiable depuis le backend 0.0.22",
            "type": "string"
          }
        },
        "type": "object"
      }
    }
  },
  "info": {
    "description": "API REST des instances [vigilo-backend](https://github.com/jesuisundesdeux/vigilo-backend).\n\nChaque territoire a sa propre instance : choisissez-la dans la liste **Servers** ci-dessous,\npuis indiquez son **scope** (affiché sur la page de chaque ville). La liste des instances\nest publiée dans [vigilo-conf](https://github.com/jesuisundesdeux/vigilo-conf/blob/main/main/citylist.json).\n\nLes observations sont des données ouvertes sous licence CC0. Les appels en lecture (`GET`) peuvent\nêtre testés directement depuis cette page (bouton **Try it out**).\n\nEn cas d'erreur, les méthodes répondent avec le code HTTP correspondant et un objet\n[`Error`](#/components/schemas/Error). La colonne « Compatibilité » de l'ancienne documentation\nest reprise dans les descriptions (« backend ≥ 0.0.x »).\n\nAuthentification : `key` est la clé privée d'un compte de l'instance (admin ou modérateur pour les droits\nsupplémentaires de cette API) ; `secretid` est la clé secrète d'une observation, retournée à sa création, qui\nidentifie son auteur. Référence détaillée :\n[doc/REST_API.md](https://github.com/jesuisundesdeux/vigilo-backend/blob/master/doc/REST_API.md).\n",
    "license": {
      "name": "AGPL-3.0 (code) / CC0 (données)",
      "url": "https://github.com/jesuisundesdeux/vigilo-backend/blob/master/LICENSE"
    },
    "title": "API Vigilo",
    "version": "0.0.26"
  },
  "openapi": "3.0.3",
  "paths": {
    "/acl.php": {
      "get": {
        "operationId": "acl",
        "parameters": [
          {
            "$ref": "#/components/parameters/key"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "role": {
                      "description": "`admin`, `moderator` ou `citystaff` ; `false` si la clé est inconnue",
                      "oneOf": [
                        {
                          "example": "admin",
                          "type": "string"
                        },
                        {
                          "example": false,
                          "type": "boolean"
                        }
                      ]
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Rôle"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Rôle associé à une clé",
        "tags": [
          "Modération"
        ]
      }
    },
    "/add_image.php": {
      "post": {
        "description": "JPEG uniquement, réduit à 1024 × 1024 pixels au plus ; remplace la photo précédente. Une observation\napprouvée ne peut plus changer de photo sans clé admin/modérateur (403 `ALREADYAPPROVED`, backend ≥ 0.0.22).\nSi l'instance a un serveur de floutage, visages et plaques sont masqués avant l'enregistrement (en-tête\n`X-Vigilo-Blur: done`, ou `failed` : photo enregistrée telle qu'envoyée).\n",
        "operationId": "addImage",
        "parameters": [
          {
            "$ref": "#/components/parameters/token"
          },
          {
            "description": "Clé secrète retournée par `create_issue.php`",
            "in": "query",
            "name": "secretid",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Photo de l'observation ou de sa résolution (backend ≥ 0.0.14)",
            "in": "query",
            "name": "type",
            "schema": {
              "default": "obs",
              "enum": [
                "obs",
                "resolution"
              ],
              "type": "string"
            }
          },
          {
            "description": "`stdin` : JPEG brut dans le corps (défaut) ; `base64` : champ `imagebin64` (backend ≥ 0.0.16)",
            "in": "query",
            "name": "method",
            "schema": {
              "default": "stdin",
              "enum": [
                "stdin",
                "base64"
              ],
              "type": "string"
            }
          },
          {
            "description": "Clé admin/modérateur, pour remplacer la photo d'une observation approuvée (backend ≥ 0.0.22)",
            "in": "query",
            "name": "key",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "description": "Backend ≥ 0.0.22, avec `method=base64`",
                "properties": {
                  "imagebin64": {
                    "description": "JPEG en base64 (préfixe `data:image/jpeg;base64,` accepté)",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "properties": {
                  "imagebin64": {
                    "description": "JPEG encodé en base64 (avec `method=base64`)",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            },
            "image/jpeg": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Status"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Envoyer la photo d'une observation",
        "tags": [
          "Contribution"
        ]
      }
    },
    "/approve.php": {
      "get": {
        "description": "Clé admin ou modérateur. À l'approbation, les webhooks configurés dans l'admin de l'instance sont appelés\n(backend ≥ 0.0.22). Réponse `{\"status\": \"0\"}`.\n",
        "operationId": "approveIssue",
        "parameters": [
          {
            "$ref": "#/components/parameters/token"
          },
          {
            "$ref": "#/components/parameters/key"
          },
          {
            "description": "0 : à modérer ; 1 : approuvée (par défaut) ; 2 : refusée",
            "in": "query",
            "name": "approved",
            "schema": {
              "default": 1,
              "enum": [
                0,
                1,
                2
              ],
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Status"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Approuver ou refuser une observation",
        "tags": [
          "Modération"
        ]
      }
    },
    "/create_issue.php": {
      "post": {
        "description": "Première étape de l'ajout d'une observation, suivie de l'envoi de la photo\n([`add_image.php`](#/Contribution/addImage)). Retourne le `token` de l'observation et sa clé\nsecrète `secretid`, à conserver pour la compléter ou la supprimer.\nAvec `token` et une clé admin/modérateur, modifie une observation existante. Les coordonnées doivent être\ndans le rectangle du scope. Limitation par IP (backend ≥ 0.0.22) : 429 `RATELIMITED` avec\n`Retry-After: 600` au-delà du seuil de l'instance sur 10 minutes, sauf avec une clé admin/modérateur.\n",
        "operationId": "createIssue",
        "parameters": [
          {
            "$ref": "#/components/parameters/keyOptional"
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "properties": {
                  "address": {
                    "type": "string"
                  },
                  "categorie": {
                    "description": "Identifiant de catégorie",
                    "type": "integer"
                  },
                  "cityid": {
                    "description": "Identifiant de la commune (backend ≥ 0.0.13)",
                    "type": "integer"
                  },
                  "cityname": {
                    "description": "Nom de la commune (backend ≥ 0.0.13)",
                    "type": "string"
                  },
                  "comment": {
                    "description": "Remarque courte (tronquée à 50 caractères)",
                    "maxLength": 50,
                    "type": "string"
                  },
                  "coordinates_lat": {
                    "example": "43.6109",
                    "type": "string"
                  },
                  "coordinates_lon": {
                    "example": "3.8772",
                    "type": "string"
                  },
                  "explanation": {
                    "type": "string"
                  },
                  "scope": {
                    "type": "string"
                  },
                  "time": {
                    "description": "Timestamp Unix en millisecondes (13 chiffres) ou en secondes",
                    "type": "integer"
                  },
                  "token": {
                    "description": "Uniquement pour modifier une observation (avec `key`)",
                    "type": "string"
                  },
                  "version": {
                    "description": "Version du client",
                    "type": "string"
                  }
                },
                "required": [
                  "coordinates_lat",
                  "coordinates_lon",
                  "categorie",
                  "address",
                  "time",
                  "scope"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "group": {
                      "deprecated": true,
                      "type": "integer"
                    },
                    "secretid": {
                      "type": "string"
                    },
                    "status": {
                      "deprecated": true,
                      "type": "integer"
                    },
                    "token": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Observation créée"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Créer (ou modifier) une observation",
        "tags": [
          "Contribution"
        ]
      }
    },
    "/create_resolution.php": {
      "post": {
        "description": "Indique qu'une ou plusieurs observations sont résolues (backend ≥ 0.0.14) ; la résolution est créée avec le\nstatut 4 (« indiquée comme résolue »), à valider dans l'admin. Envoyer ensuite la photo avec\n`add_image.php?type=resolution`. Limitation par IP (429 `RATELIMITED`, backend ≥ 0.0.22), sauf avec une clé admin.\n",
        "operationId": "createResolution",
        "parameters": [
          {
            "$ref": "#/components/parameters/keyOptional"
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "properties": {
                  "comment": {
                    "maxLength": 50,
                    "type": "string"
                  },
                  "time": {
                    "description": "Timestamp Unix en millisecondes ou en secondes",
                    "type": "integer"
                  },
                  "tokenlist": {
                    "description": "Tokens des observations résolues, séparés par des virgules",
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  }
                },
                "required": [
                  "time",
                  "tokenlist"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "secretid": {
                      "type": "string"
                    },
                    "token": {
                      "example": "R_4F2A9C1B",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Résolution créée"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Créer une résolution",
        "tags": [
          "Contribution"
        ]
      }
    },
    "/delete.php": {
      "get": {
        "description": "Avec la clé secrète de l'observation (son auteur) ou une clé admin/modérateur. Supprime l'observation et sa photo.",
        "operationId": "deleteIssue",
        "parameters": [
          {
            "$ref": "#/components/parameters/token"
          },
          {
            "$ref": "#/components/parameters/secretidOptional"
          },
          {
            "$ref": "#/components/parameters/keyOptional"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Status"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Supprimer une observation",
        "tags": [
          "Contribution"
        ]
      }
    },
    "/generate_panel.php": {
      "get": {
        "description": "Depuis le backend 0.0.22, renvoie la photo de l'observation (le panneau composé photo, carte et textes n'est\nplus généré), pixelisée tant qu'elle n'est pas approuvée, sauf avec le `secretid` de l'auteur ou une clé\nadmin/modérateur. JPEG depuis le backend 0.0.5 (PNG avant). Observation sans photo : 404 `PHOTONOTFOUND`\n(backend ≥ 0.0.23), afficher une image par défaut.\n",
        "operationId": "generatePanel",
        "parameters": [
          {
            "$ref": "#/components/parameters/token"
          },
          {
            "description": "Largeur maximale de l'image en pixels (1 à 1024, 1024 par défaut)",
            "example": 800,
            "in": "query",
            "name": "s",
            "schema": {
              "maximum": 1024,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/keyOptional"
          },
          {
            "$ref": "#/components/parameters/secretidOptional"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "image/jpeg": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Image"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Image de l'observation (ex-panneau)",
        "tags": [
          "Observations"
        ]
      }
    },
    "/get_categories.php": {
      "get": {
        "description": "Backend ≥ 0.0.23. Liste nationale ([vigilo-conf](https://raw.githubusercontent.com/jesuisundesdeux/vigilo-conf/main/main/categorielist.json)),\noù les catégories désactivées par l'instance ont `catdisable: true` (elles restent listées pour nommer les\nobservations existantes), suivie des catégories propres à l'instance (`catcustom: true`, numéros à partir\nde 1000). Réponse mise en cache 5 minutes. Sur une instance antérieure, la route répond 404 : utiliser la\nliste nationale de vigilo-conf.\n",
        "operationId": "getCategories",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Category"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Catégories"
          }
        },
        "summary": "Catégories de l'instance",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/get_issues.php": {
      "get": {
        "description": "Observations du scope dont la photo a été reçue, les plus récentes d'abord : les approuvées, plus celles à\nmodérer si l'instance publie les observations non modérées ou avec une clé admin/modérateur. Les\nobservations résolues depuis plus de N jours peuvent être masquées par l'instance (réglage, backend ≥ 0.0.22),\nsauf avec une clé. Un paramètre invalide donne une erreur 500 sans corps JSON. Backend ≥ 0.0.1 ;\nla compatibilité de chaque filtre est indiquée dans sa description.\n",
        "operationId": "getIssues",
        "parameters": [
          {
            "$ref": "#/components/parameters/scopeOptional"
          },
          {
            "description": "Format de sortie (backend ≥ 0.0.3)",
            "in": "query",
            "name": "format",
            "schema": {
              "default": "json",
              "enum": [
                "json",
                "geojson",
                "csv"
              ],
              "type": "string"
            }
          },
          {
            "description": "Nombre maximum d'observations (appliqué avant les filtres `status`, `lat`/`lon`/`radius` et `tokenfilters`)",
            "example": 20,
            "in": "query",
            "name": "count",
            "schema": {
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Filtre par catégorie ; plusieurs identifiants séparés par des virgules",
            "example": "2,3",
            "in": "query",
            "name": "c",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filtre par statut de résolution (voir `Issue.status`)",
            "in": "query",
            "name": "status",
            "schema": {
              "enum": [
                0,
                1,
                2,
                3,
                4
              ],
              "type": "integer"
            }
          },
          {
            "description": "Observations postérieures à ce timestamp Unix (backend ≥ 0.0.5)",
            "in": "query",
            "name": "t",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Filtre relatif sur la date, en nombre d'unités `since_unit` (backend ≥ 0.0.19)",
            "in": "query",
            "name": "since",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Unité du filtre `since` (backend ≥ 0.0.19)",
            "in": "query",
            "name": "since_unit",
            "schema": {
              "enum": [
                "day",
                "week",
                "month",
                "year"
              ],
              "type": "string"
            }
          },
          {
            "description": "Une observation précise (même archivée)",
            "in": "query",
            "name": "token",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`1` : inclut les observations archivées depuis l'admin (statistiques), avec le champ `archived` ;\nsans ce paramètre, elles ne sont pas listées (backend ≥ 0.0.29)\n",
            "in": "query",
            "name": "archived",
            "schema": {
              "enum": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "description": "Avec `token` : observations similaires, selon les filtres `distance`, `categorie` et/ou `address`\nséparés par des virgules (backend ≥ 0.0.9)\n",
            "example": "distance,categorie",
            "in": "query",
            "name": "tokenfilters",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Distance en mètres, obligatoire avec le filtre `distance` de `tokenfilters`",
            "in": "query",
            "name": "fdistance",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Latitude du centre de recherche (avec `lon` et `radius`)",
            "in": "query",
            "name": "lat",
            "schema": {
              "format": "double",
              "type": "number"
            }
          },
          {
            "description": "Longitude du centre de recherche",
            "in": "query",
            "name": "lon",
            "schema": {
              "format": "double",
              "type": "number"
            }
          },
          {
            "description": "Rayon de recherche en mètres autour de `lat`/`lon` (les trois sont nécessaires ; ajoute le champ `distance`)",
            "in": "query",
            "name": "radius",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Filtre selon l'approbation (backend ≥ 0.0.10). Sans clé admin/modérateur, seul `1` est accepté (et `0` si\nl'instance publie les observations non modérées) ; sinon la liste est vide.\n",
            "in": "query",
            "name": "approved",
            "schema": {
              "enum": [
                0,
                1,
                2
              ],
              "type": "integer"
            }
          },
          {
            "description": "Filtre par identifiant de commune (backend ≥ 0.0.13)",
            "in": "query",
            "name": "cityid",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "1 : la commune est retournée dans `cityname` au lieu d'être ajoutée à l'adresse (backend ≥ 0.0.13)",
            "in": "query",
            "name": "cityfield",
            "schema": {
              "enum": [
                0,
                1
              ],
              "type": "integer"
            }
          },
          {
            "description": "Clé admin/modérateur (backend ≥ 0.0.13) - observations à modérer et résolues masquées",
            "in": "query",
            "name": "key",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "items": {
                        "$ref": "#/components/schemas/Issue"
                      },
                      "type": "array"
                    },
                    {
                      "$ref": "#/components/schemas/IssueFeatureCollection"
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Observations"
          }
        },
        "summary": "Liste des observations",
        "tags": [
          "Observations"
        ]
      }
    },
    "/get_photo.php": {
      "get": {
        "description": "Photo de l'observation (ou de sa résolution avec `type=resolution`). Accessible pour les\nobservations approuvées et les résolutions, ou avec une clé admin/modérateur, ou avec un lien signé\n`exp` + `sig` (variable `{{photo_full_url}}` des webhooks, backend ≥ 0.0.26) ; 403 `NOTALLOWED` sinon\n(version pixelisée : `generate_panel.php`). Pas de photo : 404 `PHOTONOTFOUND` (backend ≥ 0.0.23).\n",
        "operationId": "getPhoto",
        "parameters": [
          {
            "$ref": "#/components/parameters/token"
          },
          {
            "description": "Photo de l'observation ou de la résolution (backend ≥ 0.0.14)",
            "in": "query",
            "name": "type",
            "schema": {
              "default": "obs",
              "enum": [
                "obs",
                "resolution"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/keyOptional"
          },
          {
            "description": "Lien signé : date d'expiration (timestamp Unix), avec `sig` (backend ≥ 0.0.26)",
            "in": "query",
            "name": "exp",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Lien signé : HMAC-SHA256 de `token|exp` par un secret de l'instance (backend ≥ 0.0.26)",
            "in": "query",
            "name": "sig",
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "image/jpeg": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Photo"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Photo originale de l'observation",
        "tags": [
          "Observations"
        ]
      }
    },
    "/get_scope.php": {
      "get": {
        "description": "Backend ≥ 0.0.4.",
        "operationId": "getScope",
        "parameters": [
          {
            "$ref": "#/components/parameters/scope"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Scope"
                }
              }
            },
            "description": "Informations du scope"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        },
        "summary": "Informations sur le scope (territoire)",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/get_version.php": {
      "get": {
        "operationId": "getVersion",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "version": {
                      "example": "0.0.26",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Version"
          }
        },
        "summary": "Version du backend",
        "tags": [
          "Configuration"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Aix Marseille Provence Metropole — scope 13_aixmarseillemetro",
      "url": "https://vigilo.velosenville.org"
    },
    {
      "description": "Evian-les-Bains — scope 74_Evian",
      "url": "https://www.modouce.fr/vigilo"
    },
    {
      "description": "Evreux — scope 27_evreux",
      "url": "https://www.vigilo.cube27.org"
    },
    {
      "description": "Grand Montpellier — scope 34_montpellier",
      "url": "https://prod-mtp.vigilo.velocite-montpellier.fr"
    },
    {
      "description": "Laval  — scope 53_laval",
      "url": "https://vigilo.placeauvelo.org"
    },
    {
      "description": "Lorient Agglo — scope 56_lorient",
      "url": "https://vigilo.claav.fr"
    },
    {
      "description": "Nantes Métropole — scope 44_nantesmetropole",
      "url": "https://vigilo.placeauvelo-nantes.fr"
    },
    {
      "description": "Paris Saclay — scope 91_parissaclay",
      "url": "https://api.vigilo-parissaclay.mdb-idf.org"
    },
    {
      "description": "Pays de Châteaugiron — scope 35_Chateaugiron",
      "url": "https://pouliquen.net/vigilo"
    },
    {
      "description": "Périgueux Agglo — scope 24_AggloPgx",
      "url": "https://vigilo.velorution-perigourdine.fr"
    },
    {
      "description": "Quimper Communauté — scope 29_quimper",
      "url": "https://vigilo.kernavelo.org"
    },
    {
      "description": "Saint-Brieuc Armor Agglomération — scope 22_SBAA",
      "url": "https://api-vigilo.velo-utile.fr"
    },
    {
      "description": "Toulon - Var — scope 83_toulon",
      "url": "https://vigilo.toulon-var-deplacements.fr"
    },
    {
      "description": "Vannes — scope 56_vannes",
      "url": "https://vigilo.kaz.bzh"
    }
  ],
  "tags": [
    {
      "description": "Lecture des observations et de leurs images",
      "name": "Observations"
    },
    {
      "description": "Création d'observations, ajout de photos, résolutions",
      "name": "Contribution"
    },
    {
      "description": "Méthodes réservées aux modérateurs (clé privée)",
      "name": "Modération"
    },
    {
      "description": "Informations sur l'instance",
      "name": "Configuration"
    }
  ]
}