"""Marking the views whose answers — including their failures — are JSON. STD-09. Deciding "JSON or HTML page" from the URL path could not work here, and the audit's own recommendation ("gestion d'erreurs API par préfixe d'URL codé en dur", fix the prefixes) would not have fixed it either. Three of the sixteen JSON views sit at paths no prefix can single out: /matches//toggle-presence/ /team-matches//toggle-presence/ /teams//toggle_status/ They are interleaved with the HTML routes of the same blueprints, and the templates fetch them. Any prefix wide enough to catch them catches every page of the section with them. So the view says so itself. `wants_json_response()` in app.py reads the mark off the registered view function, and `tests/test_api_error_format.py` walks the URL map to prove that every view calling `jsonify` carries it — the mechanism that was missing before was not a better list, it was anything at all that checked the list. Usage — directly under the route decorator, above `login_required`, so the mark lands on the object the route registers:: @matches_bp.route('/api/events') @json_endpoint @login_required def api_events(): ... """ def json_endpoint(view): """Mark a view as answering in JSON, errors included. Args: view: The view function, already wrapped by any decorator below this one (`login_required` in every current case). Returns: The same object, with the mark set. Nothing is wrapped: an extra wrapper here would be one more thing between Flask and the view for no gain, and `functools.wraps` copying `__dict__` is exactly the detail that would make this fragile. """ view.returns_json = True return view