{
  "openapi": "3.0.3",
  "info": {
    "title": "CaleDoHub Public API",
    "description": "Public read-only API for the CaleDoHub ecosystem — 500+ verified professionals across 14 platforms in New Caledonia.\n\nLicense : CC-BY 4.0 — please cite `caledohub.com` when reusing data.\n\nNo API key required for read operations. Permissive CORS, 5-minute file cache.",
    "version": "1.0.0",
    "contact": {
      "name": "CaleDoHub API Support",
      "email": "contact@deep-com.com",
      "url": "https://caledohub.com/medias.html"
    },
    "license": {
      "name": "CC-BY 4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    },
    "termsOfService": "https://caledohub.com/mentions-legales.html"
  },
  "servers": [
    {
      "url": "https://caledohub.com",
      "description": "Production"
    }
  ],
  "tags": [
    {"name": "pros", "description": "Professionals directory"},
    {"name": "stats", "description": "Aggregate statistics"},
    {"name": "indexing", "description": "Search engine indexing"}
  ],
  "paths": {
    "/api/pros.json": {
      "get": {
        "tags": ["pros"],
        "summary": "List professionals",
        "description": "Returns a paginated list of professionals with optional filters. Demo entries are excluded by default unless `demo=1`.",
        "parameters": [
          {"name": "commune", "in": "query", "description": "Filter by exact commune (e.g. `Nouméa`)", "schema": {"type": "string"}, "example": "Nouméa"},
          {"name": "category", "in": "query", "description": "Filter by category (case-insensitive contains)", "schema": {"type": "string"}, "example": "Restaurant"},
          {"name": "tier", "in": "query", "description": "Filter by tier", "schema": {"type": "string", "enum": ["premium", "pro", "free"]}, "example": "premium"},
          {"name": "limit", "in": "query", "description": "Maximum results (1-200)", "schema": {"type": "integer", "default": 100, "minimum": 1, "maximum": 200}},
          {"name": "demo", "in": "query", "description": "Include demo (seed) entries (`1` = yes)", "schema": {"type": "integer", "enum": [0, 1], "default": 0}}
        ],
        "responses": {
          "200": {
            "description": "List of pros",
            "headers": {
              "X-Cache": {"schema": {"type": "string", "enum": ["HIT", "MISS"]}},
              "Cache-Control": {"schema": {"type": "string", "example": "public, max-age=300"}}
            },
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/ProsResponse"},
                "example": {
                  "meta": {
                    "api": "caledohub-pros-v1",
                    "updated_at": "2026-05-15T08:00:00+00:00",
                    "count": 50,
                    "limit": 100,
                    "filters": {"commune": "Nouméa"},
                    "license": "CC-BY 4.0 — citez caledohub.com"
                  },
                  "pros": [
                    {
                      "id": "024fmm2oe5DARTECkuyH",
                      "name": "Garage Bertuzzi Umberto",
                      "category": "auto-moto",
                      "subcategory": "Garage/Garagiste /Mécanique générale",
                      "commune": "Nouméa",
                      "phone": "+687 28.45.12",
                      "website": "",
                      "tier": "free",
                      "verified": false,
                      "source": "plan.nc",
                      "address": "249 rue Armand Ohlen",
                      "isDemo": false
                    }
                  ]
                }
              }
            }
          },
          "503": {"$ref": "#/components/responses/Upstream"}
        }
      }
    },
    "/api/stats.json": {
      "get": {
        "tags": ["stats"],
        "summary": "Real-time aggregate statistics",
        "description": "Returns global stats : total pros, verified count, top communes/categories/tiers.",
        "responses": {
          "200": {
            "description": "Aggregate stats",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/StatsResponse"},
                "example": {
                  "meta": {
                    "api": "caledohub-stats-v1",
                    "updated_at": "2026-05-15T08:00:00+00:00",
                    "license": "CC-BY 4.0"
                  },
                  "total_pros": 300,
                  "verified_pros": 0,
                  "communes_count": 14,
                  "categories_count": 15,
                  "by_commune": {"Nouméa": 193, "Dumbéa": 25},
                  "by_category": {"auto-moto": 67, "Médecin Généraliste": 64},
                  "by_tier": {"premium": 0, "pro": 0, "free": 300}
                }
              }
            }
          }
        }
      }
    },
    "/api/communes.json": {
      "get": {
        "tags": ["stats"],
        "summary": "Distribution by commune",
        "description": "Returns pro counts per commune (alias of /api/stats.json with slice).",
        "responses": {
          "200": {"description": "Same as /api/stats.json"}
        }
      }
    },
    "/api/categories.json": {
      "get": {
        "tags": ["stats"],
        "summary": "Top categories",
        "description": "Returns pro counts per category (alias of /api/stats.json with slice).",
        "responses": {
          "200": {"description": "Same as /api/stats.json"}
        }
      }
    },
    "/api/reviews.json": {
      "get": {
        "tags": ["pros"],
        "summary": "Approved reviews",
        "description": "Returns approved reviews (status='approved'). Optional filter by proId.\n\n⚠️ Currently returns 503 if Firestore rules forbid public read. Requires Firestore rule : `match /reviews/{rev} { allow read: if resource.data.status == 'approved'; }`",
        "parameters": [
          {"name": "proId", "in": "query", "description": "Filter by specific pro ID", "schema": {"type": "string"}},
          {"name": "limit", "in": "query", "description": "Max results (1-100)", "schema": {"type": "integer", "default": 50, "maximum": 100}}
        ],
        "responses": {
          "200": {"description": "List of approved reviews + aggregates"},
          "503": {"description": "Firestore rules prevent anonymous read — rules update needed"}
        }
      }
    },
    "/api/indexnow.json": {
      "get": {
        "tags": ["indexing"],
        "summary": "Single URL IndexNow ping",
        "description": "Ping a single URL to Bing/Yandex via IndexNow. Restricted to caledo*.com hosts.",
        "parameters": [
          {"name": "url", "in": "query", "required": true, "description": "Full URL to submit", "schema": {"type": "string", "format": "uri"}}
        ],
        "responses": {
          "200": {"description": "Submitted to IndexNow API"},
          "403": {"description": "Host not in caledo*.com allowlist"}
        }
      },
      "post": {
        "tags": ["indexing"],
        "summary": "Batch URL IndexNow ping",
        "description": "Submit up to 1000 URLs in one batch. Requires `X-IndexNow-Secret` header matching site key.",
        "parameters": [
          {"name": "X-IndexNow-Secret", "in": "header", "required": true, "schema": {"type": "string"}, "description": "Site IndexNow key"}
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {"urls": {"type": "array", "items": {"type": "string", "format": "uri"}, "maxItems": 1000}},
                "required": ["urls"]
              }
            }
          }
        },
        "responses": {
          "200": {"description": "Submitted (grouped by host)"},
          "401": {"description": "Missing/invalid X-IndexNow-Secret header"}
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Pro": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "description": "Firestore document ID"},
          "name": {"type": "string"},
          "category": {"type": "string"},
          "subcategory": {"type": "string"},
          "commune": {"type": "string"},
          "phone": {"type": "string"},
          "website": {"type": "string"},
          "tier": {"type": "string", "enum": ["premium", "pro", "free"]},
          "verified": {"type": "boolean"},
          "source": {"type": "string"},
          "address": {"type": "string"},
          "isDemo": {"type": "boolean"}
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "api": {"type": "string"},
          "updated_at": {"type": "string", "format": "date-time"},
          "license": {"type": "string", "example": "CC-BY 4.0 — citez caledohub.com"},
          "documentation": {"type": "string", "format": "uri"}
        }
      },
      "ProsResponse": {
        "type": "object",
        "properties": {
          "meta": {"$ref": "#/components/schemas/Meta"},
          "pros": {"type": "array", "items": {"$ref": "#/components/schemas/Pro"}}
        }
      },
      "StatsResponse": {
        "type": "object",
        "properties": {
          "meta": {"$ref": "#/components/schemas/Meta"},
          "total_pros": {"type": "integer"},
          "verified_pros": {"type": "integer"},
          "communes_count": {"type": "integer"},
          "categories_count": {"type": "integer"},
          "by_commune": {"type": "object", "additionalProperties": {"type": "integer"}},
          "by_category": {"type": "object", "additionalProperties": {"type": "integer"}},
          "by_tier": {"type": "object", "properties": {"premium": {"type": "integer"}, "pro": {"type": "integer"}, "free": {"type": "integer"}}},
          "platforms": {"type": "array", "items": {"type": "string"}}
        }
      }
    },
    "responses": {
      "Upstream": {
        "description": "Upstream Firestore unavailable",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {"type": "string", "example": "upstream_unavailable"},
                "detail": {"type": "string"}
              }
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Full documentation & ecosystem",
    "url": "https://caledohub.com/a-propos.html"
  }
}
