openapi: 3.0.3
info:
  title: Deklip API
  version: "1.0"
  description: |
    API publique Deklip : génération de vidéos courtes virales par IA (briefs → angles → vidéos),
    clipping de vidéos longues, posts images et avatars UGC.

    Auth : `Authorization: Bearer dk_live_…` (clé créée dans Mon compte → API).
    Scopes par clé : `read`, `generate`, `edit`.
    Générations longues : réponse immédiate avec `job_id`, suivre via `GET /jobs/{id}`
    (ou webhooks signés HMAC `job.completed` / `job.failed`).
    Chaque réponse facturée inclut `cost` (crédits débités) et `credits_remaining`.
    Idempotence : header `Idempotency-Key` sur les POST (rejoue la même réponse 24 h).
    Limites : 100 req/min par clé, plafond de crédits journalier par clé (défaut 500).
servers:
  - url: https://deklip.com/v1
security:
  - bearer: []
tags:
  - name: compte
  - name: briefs
  - name: angles
  - name: jobs
  - name: editeur
  - name: clips
  - name: posts
  - name: avatar
  - name: planning
paths:
  /me:
    get: {tags: [compte], summary: Compte (email, crédits, plan), responses: {"200": {$ref: "#/components/responses/OK"}}}
  /pricing:
    get: {tags: [compte], summary: Grille des coûts par action, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /usage:
    get:
      tags: [compte]
      summary: Usage API (requêtes, crédits, journal)
      parameters: [{name: days, in: query, schema: {type: integer, maximum: 90}}]
      responses: {"200": {$ref: "#/components/responses/OK"}}
  /voices:
    get: {tags: [compte], summary: Voix off disponibles, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /webhooks:
    get: {tags: [compte], summary: Webhooks configurés, responses: {"200": {$ref: "#/components/responses/OK"}}}
    post:
      tags: [compte]
      summary: Ajouter un webhook (job.completed, job.failed), signé HMAC-SHA256 (X-Deklip-Signature)
      requestBody: {$ref: "#/components/requestBodies/JSON"}
      responses: {"200": {$ref: "#/components/responses/OK"}}
  /webhooks/{id}:
    delete: {tags: [compte], summary: Supprimer un webhook, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /briefs:
    get: {tags: [briefs], summary: Lister les briefs, responses: {"200": {$ref: "#/components/responses/OK"}}}
    post:
      tags: [briefs]
      summary: Créer un brief (name, product, platform, language…)
      requestBody: {$ref: "#/components/requestBodies/JSON"}
      responses: {"200": {$ref: "#/components/responses/OK"}}
  /briefs/{id}:
    get: {tags: [briefs], summary: Lire un brief, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
    put: {tags: [briefs], summary: Modifier un brief, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
    delete: {tags: [briefs], summary: Supprimer un brief, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /briefs/{id}/angles:
    get: {tags: [angles], summary: Angles d'un brief, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
    post: {tags: [angles], summary: "Générer une série d'angles (10 cr)", parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /brief-scan:
    post: {tags: [briefs], summary: Créer un brief depuis une capture d'écran, requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /angles/{id}:
    put: {tags: [angles], summary: Modifier un angle, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
    delete: {tags: [angles], summary: Supprimer un angle, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /angles/{id}/perf:
    post: {tags: [angles], summary: Remonter les stats (vues, rétention…), parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /angles/{id}/perf-scan:
    post: {tags: [angles], summary: Extraire les stats d'une capture analytics, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /angles/{id}/iterate:
    post: {tags: [angles], summary: Décliner un angle gagnant, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /angles/{id}/video:
    post: {tags: [jobs], summary: "Générer la vidéo d'un angle (40 cr, +30 hook clip) — ASYNC → job_id", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/Job"}}}
  /jobs:
    get: {tags: [jobs], summary: Lister les jobs, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /dashboard:
    get: {tags: [jobs], summary: "Contenus organisés (vidéos IA, clips, imports, avatars)", responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}:
    get: {tags: [jobs], summary: "Statut + progression + URL résultat", parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/Job"}}}
    delete: {tags: [jobs], summary: Supprimer, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/download:
    get: {tags: [jobs], summary: Télécharger le MP4, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {description: MP4}}}
  /jobs/{id}/duplicate:
    post: {tags: [jobs], summary: Dupliquer, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/hooks:
    post: {tags: [jobs], summary: "Variantes A/B de hook (15 cr/variante) — ASYNC", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/viralcheck:
    post: {tags: [jobs], summary: Score viral + recommandations, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/pubkit:
    post: {tags: [jobs], summary: Kit de publication (titre, hashtags…), parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/share:
    post: {tags: [jobs], summary: Lien de partage public, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/published:
    post: {tags: [jobs], summary: Marquer publiée, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/timeline:
    get: {tags: [editeur], summary: Lire le montage (timeline JSON), parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
    put: {tags: [editeur], summary: Écrire le montage, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/rerender:
    post: {tags: [editeur], summary: "Re-monter la vidéo (5 cr) — ASYNC", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/revoice:
    post: {tags: [editeur], summary: "Changer la voix off (5 cr)", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/recaption:
    post: {tags: [editeur], summary: "Resynchroniser les sous-titres (2 cr)", parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/scene-image:
    post: {tags: [editeur], summary: "Régénérer un visuel de scène (2 cr)", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/scene-video:
    post: {tags: [editeur], summary: "Générer un clip vidéo de scène (30 cr) — ASYNC", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/animate-scene:
    post: {tags: [editeur], summary: "Animer une scène image (30 cr) — ASYNC", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/media:
    post: {tags: [editeur], summary: Uploader un média de scène, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/stock-replace:
    post: {tags: [editeur], summary: Remplacer un visuel par du stock, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/music:
    post: {tags: [editeur], summary: "Musique IA (5 cr)", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/translate:
    post: {tags: [editeur], summary: "Traduire les sous-titres (3 cr)", parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/beatsync:
    post: {tags: [editeur], summary: Caler les coupes sur la musique, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/assistant:
    post: {tags: [editeur], summary: Copilote IA d'édition (gratuit), parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /jobs/{id}/recut:
    post: {tags: [clips], summary: Ajuster les bornes d'un clip, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /clips:
    post: {tags: [clips], summary: "Découper une vidéo longue depuis une URL (30 cr) — ASYNC → job_id", requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/Job"}}}
  /clips/{id}:
    get: {tags: [clips], summary: Session de clipping + clips produits, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /import-video:
    post: {tags: [clips], summary: "Importer un fichier vidéo (20 cr) — ASYNC", responses: {"200": {$ref: "#/components/responses/Job"}}}
  /posts:
    get: {tags: [posts], summary: Lister les posts images, responses: {"200": {$ref: "#/components/responses/OK"}}}
    post: {tags: [posts], summary: Créer un post image — ASYNC, requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /posts/{id}:
    get: {tags: [posts], summary: Lire un post, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
    put: {tags: [posts], summary: Modifier un post, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /posts/{id}/copy:
    post: {tags: [posts], summary: Régénérer la copie, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /posts/{id}/image:
    post: {tags: [posts], summary: Régénérer le fond, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /posts/{id}/render:
    post: {tags: [posts], summary: Rendre le PNG final, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /avatar:
    post: {tags: [avatar], summary: "Vidéo avatar UGC lip-sync (90 cr) — ASYNC → job_id", requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/Job"}}}
  /studio:
    post: {tags: [jobs], summary: Vidéo studio libre — ASYNC, requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/Job"}}}
  /schedule:
    get: {tags: [planning], summary: Calendrier de publication, responses: {"200": {$ref: "#/components/responses/OK"}}}
    post: {tags: [planning], summary: Planifier une publication, requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /schedule/{id}:
    post: {tags: [planning], summary: Modifier, parameters: [{$ref: "#/components/parameters/id"}], requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
    delete: {tags: [planning], summary: Supprimer, parameters: [{$ref: "#/components/parameters/id"}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /brand/assets:
    get: {tags: [compte], summary: Bibliothèque de marque, responses: {"200": {$ref: "#/components/responses/OK"}}}
    post: {tags: [compte], summary: Ajouter un asset, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /brand/kit:
    get: {tags: [compte], summary: Brand kit (couleur, logo), responses: {"200": {$ref: "#/components/responses/OK"}}}
    post: {tags: [compte], summary: Définir le brand kit, requestBody: {$ref: "#/components/requestBodies/JSON"}, responses: {"200": {$ref: "#/components/responses/OK"}}}
  /stock:
    get: {tags: [editeur], summary: Recherche banque vidéo/photo, parameters: [{name: q, in: query, schema: {type: string}}], responses: {"200": {$ref: "#/components/responses/OK"}}}
  /music-bank:
    get: {tags: [editeur], summary: Banque musicale, parameters: [{name: q, in: query, schema: {type: string}}], responses: {"200": {$ref: "#/components/responses/OK"}}}
components:
  securitySchemes:
    bearer: {type: http, scheme: bearer, bearerFormat: "dk_live_…"}
  parameters:
    id: {name: id, in: path, required: true, schema: {type: string}}
  requestBodies:
    JSON:
      content: {application/json: {schema: {type: object}}}
  responses:
    OK:
      description: Succès. Les actions facturées incluent `cost` et `credits_remaining`.
      content: {application/json: {schema: {type: object}}}
    Job:
      description: Job asynchrone créé — suivre GET /jobs/{id} jusqu'à status=done.
      content:
        application/json:
          schema:
            type: object
            properties:
              id: {type: string}
              status: {type: string, enum: [processing, done, error]}
              cost: {type: integer}
              credits_remaining: {type: integer}
