{
  "openapi": "3.1.0",
  "info": {
    "title": "BoostCore Public API",
    "version": "1.0.0",
    "summary": "API de catalogue, wallet et commandes SMM BoostCore.",
    "description": "API gratuite pour consulter le catalogue, obtenir un devis en credits, creer et suivre des commandes, demander une annulation ou un refill et recevoir des webhooks. Les montants de l'API sont exprimes en credits BoostCore. Le catalogue tarifaire avec les prix en credits et leurs equivalents USD est disponible apres connexion dans l'onglet Developpeur de l'espace client : https://www.boostcore.tech/connexion. Les paiements et recharges ne font pas partie de cette API."
  },
  "servers": [
    {
      "url": "https://www.boostcore.tech/api/v1",
      "description": "Production"
    }
  ],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "Catalogue" },
    { "name": "Wallet" },
    { "name": "Commandes" },
    { "name": "Webhooks" }
  ],
  "paths": {
    "/services": {
      "get": {
        "tags": ["Catalogue"],
        "summary": "Lister les services publies",
        "operationId": "listServices",
        "x-required-scope": "catalog:read",
        "responses": {
          "200": { "description": "Catalogue public dont les tarifs API sont en credits BoostCore. Les equivalents USD sont visibles apres connexion dans l'onglet Developpeur de l'espace client.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServicesResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/quotes": {
      "post": {
        "tags": ["Catalogue"],
        "summary": "Calculer un devis sans debit",
        "operationId": "createQuote",
        "x-required-scope": "catalog:read",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteRequest" } } }
        },
        "responses": {
          "200": { "description": "Devis calcule en credits BoostCore. L'equivalent tarifaire USD est visible dans l'espace client connecte, onglet Developpeur.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/balance": {
      "get": {
        "tags": ["Wallet"],
        "summary": "Lire le solde du proprietaire de la cle",
        "operationId": "getBalance",
        "x-required-scope": "wallet:read",
        "responses": {
          "200": { "description": "Solde interne en credits BoostCore. Le catalogue des equivalents USD est visible dans l'espace client connecte, onglet Developpeur.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BalanceResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders": {
      "get": {
        "tags": ["Commandes"],
        "summary": "Lister les commandes API du compte",
        "operationId": "listOrders",
        "x-required-scope": "orders:read",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "$ref": "#/components/schemas/OrderStatus" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Liste paginee", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrdersResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Commandes"],
        "summary": "Creer une commande",
        "description": "Le prix est recalcule cote serveur et les credits sont reserves atomiquement. Rejouer la meme requete avec la meme cle d'idempotence retourne la meme commande sans nouveau debit.",
        "operationId": "createOrder",
        "x-required-scope": "orders:write",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOrderRequest" } } }
        },
        "responses": {
          "201": { "description": "Commande creee", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderResponse" } } } },
          "200": { "description": "Rejeu idempotent de la commande existante", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderResponse" } } } },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/status": {
      "post": {
        "tags": ["Commandes"],
        "summary": "Lire jusqu'a 100 statuts",
        "operationId": "getOrderStatuses",
        "x-required-scope": "orders:read",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["order_refs"], "additionalProperties": false, "properties": { "order_refs": { "type": "array", "minItems": 1, "maxItems": 100, "uniqueItems": true, "items": { "$ref": "#/components/schemas/OrderReference" } } } } } }
        },
        "responses": {
          "200": { "description": "Statuts demandes" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/{order_ref}": {
      "get": {
        "tags": ["Commandes"],
        "summary": "Lire une commande",
        "operationId": "getOrder",
        "x-required-scope": "orders:read",
        "parameters": [{ "$ref": "#/components/parameters/OrderReference" }],
        "responses": {
          "200": { "description": "Commande et timeline assainie" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/{order_ref}/cancel": {
      "post": {
        "tags": ["Commandes"],
        "summary": "Demander une annulation",
        "operationId": "cancelOrder",
        "x-required-scope": "cancellations:write",
        "parameters": [{ "$ref": "#/components/parameters/OrderReference" }, { "$ref": "#/components/parameters/IdempotencyKey" }],
        "responses": {
          "202": { "description": "Demande mise en file" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/{order_ref}/refills": {
      "post": {
        "tags": ["Commandes"],
        "summary": "Demander un refill eligible",
        "operationId": "createRefill",
        "x-required-scope": "refills:write",
        "parameters": [{ "$ref": "#/components/parameters/OrderReference" }, { "$ref": "#/components/parameters/IdempotencyKey" }],
        "responses": {
          "202": { "description": "Refill mis en file" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders/{order_ref}/refills/{refill_ref}": {
      "get": {
        "tags": ["Commandes"],
        "summary": "Lire un refill",
        "operationId": "getRefill",
        "x-required-scope": "orders:read",
        "parameters": [
          { "$ref": "#/components/parameters/OrderReference" },
          { "name": "refill_ref", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Etat du refill" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "Lister les endpoints webhook",
        "operationId": "listWebhooks",
        "x-required-scope": "webhooks:manage",
        "responses": {
          "200": { "description": "Endpoints du compte" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Creer un endpoint webhook",
        "description": "Le secret de signature n'est retourne qu'une seule fois. URL HTTPS publique obligatoire, sans redirection.",
        "operationId": "createWebhook",
        "x-required-scope": "webhooks:manage",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookRequest" } } }
        },
        "responses": {
          "201": { "description": "Endpoint cree et secret retourne" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/webhooks/{endpoint_id}": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Desactiver un endpoint webhook",
        "operationId": "disableWebhook",
        "x-required-scope": "webhooks:manage",
        "parameters": [{ "$ref": "#/components/parameters/EndpointId" }],
        "responses": {
          "200": { "description": "Endpoint desactive" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/webhooks/{endpoint_id}/deliveries": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "Lister les livraisons webhook",
        "operationId": "listWebhookDeliveries",
        "x-required-scope": "webhooks:manage",
        "parameters": [
          { "$ref": "#/components/parameters/EndpointId" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } }
        ],
        "responses": {
          "200": { "description": "Livraisons recentes" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/webhooks/{endpoint_id}/test": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Mettre un webhook de test en file",
        "operationId": "testWebhook",
        "x-required-scope": "webhooks:manage",
        "parameters": [{ "$ref": "#/components/parameters/EndpointId" }],
        "responses": {
          "202": { "description": "Livraison de test en attente" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "bc_live_...",
        "description": "Cle API BoostCore creee depuis le compte client."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Valeur opaque unique de 1 a 128 caracteres. Conserver la meme valeur lors d'un retry de la meme operation.",
        "schema": { "type": "string", "minLength": 1, "maxLength": 128 }
      },
      "OrderReference": {
        "name": "order_ref",
        "in": "path",
        "required": true,
        "schema": { "$ref": "#/components/schemas/OrderReference" }
      },
      "EndpointId": {
        "name": "endpoint_id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "Unauthorized": { "description": "Cle API absente, invalide, expiree ou revoquee", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "Forbidden": { "description": "Scope insuffisant", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "NotFound": { "description": "Ressource introuvable ou non detenue par le compte", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "Conflict": { "description": "Etat metier incompatible ou cle d'idempotence reutilisee avec d'autres parametres", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "ValidationError": { "description": "Parametres incompatibles avec le service", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "RateLimited": {
        "description": "Quota temporairement depasse. Respecter Retry-After.",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" } },
          "RateLimit-Limit": { "schema": { "type": "integer" } },
          "RateLimit-Remaining": { "schema": { "type": "integer" } },
          "RateLimit-Reset": { "schema": { "type": "integer" } }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      }
    },
    "schemas": {
      "OrderReference": { "type": "string", "pattern": "^ORD-[A-Z0-9]{12}$", "example": "ORD-073D5BFCF3A8" },
      "OrderStatus": { "type": "string", "enum": ["queued", "submitting", "submission_unknown", "processing", "completed", "partial", "cancelled", "failed", "refunded", "review_required"] },
      "Meta": {
        "type": "object",
        "required": ["request_id", "timestamp"],
        "properties": {
          "request_id": { "type": "string", "format": "uuid" },
          "timestamp": { "type": "string", "format": "date-time" }
        },
        "additionalProperties": true
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error", "meta"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "retryable"],
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "retryable": { "type": "boolean" },
              "field_errors": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } }
            }
          },
          "meta": { "$ref": "#/components/schemas/Meta" }
        }
      },
      "Service": {
        "type": "object",
        "required": ["service_id", "service_name", "platform_name", "category_name", "min_quantity", "max_quantity", "quantity_step", "reference_unit", "credits_per_reference", "terms_version"],
        "properties": {
          "service_id": { "type": "string", "format": "uuid" },
          "public_service_id": { "type": "integer" },
          "service_name": { "type": "string" },
          "service_description": { "type": ["string", "null"] },
          "platform_name": { "type": "string" },
          "category_name": { "type": "string" },
          "target_type": { "type": "string" },
          "min_quantity": { "type": "integer" },
          "max_quantity": { "type": "integer" },
          "quantity_step": { "type": "integer" },
          "reference_unit": { "type": "integer" },
          "credits_per_reference": { "type": "number" },
          "minimum_order_credits": { "type": "number" },
          "refill_enabled": { "type": "boolean" },
          "refill_window_days": { "type": ["integer", "null"] },
          "terms_version": { "type": "string" }
        }
      },
      "ServicesResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "type": "object", "required": ["services"], "properties": { "services": { "type": "array", "items": { "$ref": "#/components/schemas/Service" } } } },
          "meta": { "$ref": "#/components/schemas/Meta" }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "required": ["service_id", "quantity"],
        "additionalProperties": false,
        "properties": {
          "service_id": { "type": "string", "format": "uuid" },
          "quantity": { "type": "integer", "minimum": 1 }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "type": "object", "properties": { "service_id": { "type": "string", "format": "uuid" }, "quantity": { "type": "integer" }, "credits": { "type": "number" }, "terms_version": { "type": "string" } } },
          "meta": { "$ref": "#/components/schemas/Meta" }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "type": "object", "properties": { "available_credits": { "type": "number" }, "reserved_credits": { "type": "number" }, "status": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } },
          "meta": { "$ref": "#/components/schemas/Meta" }
        }
      },
      "CreateOrderRequest": {
        "type": "object",
        "required": ["service_id", "quantity", "target", "terms_version"],
        "additionalProperties": false,
        "properties": {
          "service_id": { "type": "string", "format": "uuid" },
          "quantity": { "type": "integer", "minimum": 1 },
          "target": { "type": "string", "format": "uri", "maxLength": 2048 },
          "terms_version": { "type": "string" }
        }
      },
      "Order": {
        "type": "object",
        "required": ["order_ref", "status", "financial_status", "quantity_requested", "quantity_delivered", "credits_charged"],
        "properties": {
          "order_ref": { "$ref": "#/components/schemas/OrderReference" },
          "legacy_order_id": { "type": "integer" },
          "status": { "$ref": "#/components/schemas/OrderStatus" },
          "financial_status": { "type": "string" },
          "quantity_requested": { "type": "integer" },
          "quantity_delivered": { "type": "integer" },
          "credits_charged": { "type": "number" },
          "credits_refunded": { "type": "number" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "OrderResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": { "data": { "$ref": "#/components/schemas/Order" }, "meta": { "$ref": "#/components/schemas/Meta" } }
      },
      "OrdersResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": { "type": "object", "properties": { "orders": { "type": "array", "items": { "$ref": "#/components/schemas/Order" } } } },
          "meta": { "allOf": [{ "$ref": "#/components/schemas/Meta" }, { "type": "object", "properties": { "limit": { "type": "integer" }, "offset": { "type": "integer" }, "total": { "type": "integer" } } }] }
        }
      },
      "CreateWebhookRequest": {
        "type": "object",
        "required": ["name", "url", "events"],
        "additionalProperties": false,
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 80 },
          "url": { "type": "string", "format": "uri", "pattern": "^https://" },
          "events": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "enum": ["order.*", "order.created", "order.processing", "order.completed", "order.partial", "order.cancelled", "order.failed", "order.refunded", "order.review_required", "order.cancel_requested", "order.refill_requested"] } }
        }
      }
    }
  }
}
