{
  "openapi": "3.1.0",
  "info": {
    "title": "Galon Cloud API",
    "version": "1.0.0",
    "description": "API multi-tenant pour projets, jobs asynchrones, artefacts et webhooks."
  },
  "servers": [{"url": "https://api.galon.ai/v1"}],
  "tags": [
    {"name": "Tools"}, {"name": "Projects"}, {"name": "Services"}, {"name": "Jobs"},
    {"name": "Builds"}, {"name": "Webhooks"}
  ],
  "paths": {
    "/tools": {
      "get": {"tags": ["Tools"], "summary": "Lister les outils autorisés par la clé",
        "security": [{"ApiKey": []}], "responses": {"200": {"description": "Catalogue"}, "401": {"$ref": "#/components/responses/Unauthorized"}}}
    },
    "/projects": {
      "get": {"tags": ["Projects"], "summary": "Lister les projets du tenant",
        "security": [{"Session": []}], "responses": {"200": {"description": "Projets"}}},
      "post": {"tags": ["Projects"], "summary": "Créer un projet", "security": [{"Session": []}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProjectCreate"}}}},
        "responses": {"201": {"description": "Projet créé"}, "422": {"$ref": "#/components/responses/Invalid"}}}
    },
    "/projects/{project_id}/keys": {
      "get": {"tags": ["Projects"], "summary": "Lister les clés sans leur secret", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}], "responses": {"200": {"description": "Clés"}}},
      "post": {"tags": ["Projects"], "summary": "Créer une clé API affichée une seule fois", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/KeyCreate"}}}},
        "responses": {"201": {"description": "Clé créée", "headers": {"Cache-Control": {"schema": {"type": "string"}}}}}}
    },
    "/search": {
      "post": {"tags": ["Services"], "summary": "Rechercher le web", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchCreate"}}}},
        "responses": {"202": {"description": "Recherche mise en file"}, "422": {"$ref": "#/components/responses/Invalid"}}}
    },
    "/ai/responses": {
      "post": {"tags": ["Services"], "summary": "Générer une réponse IA", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AiCreate"}}}},
        "responses": {"202": {"description": "Génération mise en file"}, "422": {"$ref": "#/components/responses/Invalid"}}}
    },
    "/jobs": {
      "post": {"tags": ["Jobs"], "summary": "Soumettre un job", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/JobCreate"}}}},
        "responses": {"202": {"description": "Job accepté"}, "409": {"description": "Conflit d'idempotence"}, "422": {"$ref": "#/components/responses/Invalid"}}}
    },
    "/jobs/{job_id}": {
      "get": {"tags": ["Jobs"], "summary": "Lire un job", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/JobId"}],
        "responses": {"200": {"description": "Job", "content": {"application/json": {"schema": {"type": "object", "properties": {"job": {"$ref": "#/components/schemas/Job"}}}}}}, "404": {"description": "Absent"}}}
    },
    "/jobs/{job_id}/cancel": {
      "post": {"tags": ["Jobs"], "summary": "Annuler un job", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/JobId"}], "responses": {"200": {"description": "Job annulé"}, "409": {"description": "Job terminal"}}}
    },
    "/builds": {
      "post": {"tags": ["Builds"], "summary": "Construire une application web", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/IdempotencyKey"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["plan"], "properties": {"plan": {"type": "object"}}}}}},
        "responses": {"202": {"description": "Build accepté"}}}
    },
    "/builds/{job_id}/artifacts": {
      "get": {"tags": ["Builds"], "summary": "Lister les artefacts d'un build", "security": [{"ApiKey": []}],
        "parameters": [{"$ref": "#/components/parameters/JobId"}], "responses": {"200": {"description": "Artefacts"}}}
    },
    "/webhook-events": {
      "get": {"tags": ["Webhooks"], "summary": "Lister les types d'événement", "security": [{"Session": []}],
        "responses": {"200": {"description": "Types fermés"}}}
    },
    "/projects/{project_id}/webhooks": {
      "get": {"tags": ["Webhooks"], "summary": "Lister les endpoints", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}], "responses": {"200": {"description": "Endpoints sans secret"}}},
      "post": {"tags": ["Webhooks"], "summary": "Créer un endpoint et afficher son secret une fois", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookCreate"}}}},
        "responses": {"201": {"description": "Endpoint créé"}, "422": {"$ref": "#/components/responses/Invalid"}}}
    },
    "/projects/{project_id}/webhook-deliveries": {
      "get": {"tags": ["Webhooks"], "summary": "Lister les livraisons", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}], "responses": {"200": {"description": "Livraisons"}}}
    },
    "/projects/{project_id}/webhook-deliveries/{delivery_id}/replay": {
      "post": {"tags": ["Webhooks"], "summary": "Rejouer une livraison terminale", "security": [{"Session": []}],
        "parameters": [{"$ref": "#/components/parameters/ProjectId"}, {"name": "delivery_id", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Remise en file"}, "404": {"description": "Non rejouable"}}}
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {"type": "http", "scheme": "bearer", "bearerFormat": "gln_test_... ou gln_live_..."},
      "Session": {"type": "apiKey", "in": "cookie", "name": "aria_session"}
    },
    "parameters": {
      "ProjectId": {"name": "project_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}},
      "JobId": {"name": "job_id", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[0-9a-f]{64}$"}},
      "IdempotencyKey": {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 200}}
    },
    "schemas": {
      "ProjectCreate": {"type": "object", "additionalProperties": false, "required": ["name"], "properties": {"name": {"type": "string", "maxLength": 100}, "environment": {"enum": ["test", "live"], "default": "test"}}},
      "KeyCreate": {"type": "object", "additionalProperties": false, "required": ["scopes"], "properties": {"environment": {"enum": ["test", "live"]}, "scopes": {"type": "array", "items": {"type": "string"}}, "expires_at": {"type": "string", "format": "date-time"}}},
      "JobCreate": {"type": "object", "additionalProperties": false, "required": ["type", "input"], "properties": {
        "type": {"enum": ["search.web", "ai.generate", "mail.send", "telecom.sms", "build.application_web", "build.application_android", "source.github_publish", "deploy.vercel_preview", "deploy.vercel_production", "database.supabase_provision", "database.supabase_migrate", "domain.lookup", "domain.purchase", "domain.connect"]},
        "input": {"type": "object"}}},
      "SearchCreate": {"type": "object", "additionalProperties": false, "required": ["query"], "properties": {"query": {"type": "string", "maxLength": 500}, "limit": {"type": "integer", "minimum": 1, "maximum": 25, "default": 10}, "language": {"type": "string", "pattern": "^[a-z]{2}(-[a-z]{2})?$"}}},
      "AiCreate": {"type": "object", "additionalProperties": false, "required": ["prompt"], "properties": {"prompt": {"type": "string", "maxLength": 12000}, "model": {"enum": ["auto", "fast", "reasoning"], "default": "auto"}, "output_limit": {"type": "integer", "minimum": 1, "maximum": 4096, "default": 512}}},
      "Job": {"type": "object", "required": ["id", "type", "statut"], "properties": {"id": {"type": "string"}, "type": {"type": "string"}, "statut": {"enum": ["en_file", "en_cours", "reussi", "echoue", "annule", "indetermine"]}, "tentatives": {"type": "integer"}, "resultat": {"type": "object"}, "erreur": {"type": "string"}}},
      "WebhookCreate": {"type": "object", "additionalProperties": false, "required": ["url", "events"], "properties": {"url": {"type": "string", "format": "uri", "pattern": "^https://"}, "events": {"type": "array", "minItems": 1, "items": {"enum": ["job.queued", "job.started", "job.succeeded", "job.failed", "job.cancelled", "job.uncertain", "artifact.ready"]}}}},
      "Error": {"type": "object", "properties": {"detail": {"type": "object", "properties": {"code": {"type": "string"}, "message": {"type": "string"}}}}}
    },
    "responses": {
      "Unauthorized": {"description": "Clé absente ou invalide", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "Invalid": {"description": "Paramètres invalides", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}
    }
  }
}
