openapi: 3.1.0
info:
  title: Webhooks marchands Ciklik
  version: '2026-09-10'
  summary: Événements sortants envoyés par Ciklik vers le système du marchand
  description: |-
    Contrat des notifications sortantes envoyées par Ciklik à votre système. Résumé du contrat en bref, détaillé dans les pages de documentation :

    - Requête `POST` en JSON compact, une URL de réception par clé d'événement.
    - Pas de signature ni de secret partagé : l'URL est le seul secret du dispositif.
    - Le nom de l'événement n'est jamais dans le corps de la requête, seulement dans l'URL que vous avez déclarée.
    - Délai avant l'envoi : 0, 10 ou 15 secondes selon l'événement (`x-ciklik-delay-seconds`).
    - 10 secondes pour établir la connexion, 30 secondes pour votre réponse.
    - 5 tentatives au maximum, et seulement sur une erreur de connexion, jamais sur un code de réponse (`x-ciklik-retries`).
    - Un doublon est possible et normal si vous répondez en plus de 30 secondes : l'envoi est alors considéré comme perdu et rejoué, alors qu'il vous est déjà parvenu.
    - Redirection interdite : sur 301, 302 ou 303, la requête est rejouée en GET et le contenu est perdu.
    - Certificat TLS valide obligatoire, émis par une autorité reconnue.
    - Aucun ordre garanti entre les envois.
    - Le contenu est relu au moment de l'envoi : il peut être plus récent que l'événement, seul `changed` décrit l'instant de l'événement.
  contact:
    name: Support Ciklik
    url: https://www.ciklik.academy/fr/webhooks/
  license:
    name: Tous droits réservés
servers:
- url: https://app.ciklik.co/api/v3
  description: API de configuration
webhooks:
  created_subscription:
    post:
      summary: Un abonnement est créé.
      description: |-
        **Quand il part** : création depuis le site marchand, import composable ou PrestaShop lors du passage d'une commande, activation d'une carte cadeau (crée un abonnement ordinaire).

        **Quand il ne part pas** : import d'abonnements par fichier CSV, que ce soit par tâche planifiée ou par commande console, quel que soit le volume.

        **Cascade** : aucune. `changed` est vide.
      operationId: createdSubscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Subscription'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              id: 1
              uuid: sub_N8PxGUEdS2U7DP
              plan:
                id: 1
                uuid: boutique-exemple-box-mensuelle-1
                name: Box mensuelle
                short_name: Mensuelle
                more: Une sélection de trois thés chaque mois
                image: https://s3.eu-central-1.amazonaws.com/ciklik-media/plans/box-mensuelle.jpg
                start_at: null
                price: 29.9
                tax: 0.2
                interval: month
                interval_count: 1
                shipped_count: 1
                position: 1
                engaged_interval: null
                engaged: false
                active: true
              address:
                id: 1
                user_id: 1
                address: 12 rue des Lilas
                address1: Bâtiment B, 3e étage
                postcode: '69003'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              end_date: '2026-10-09T00:00:00.000000Z'
              engaged_date: null
              auto_pause_at: null
              start_date: '2026-06-10T00:00:00.000000Z'
              active: true
              paused: false
              next_billing: '2026-10-10T08:15:42.000000Z'
              update_transporter_at: null
              relay: null
              graceMonths: null
              user_uuid: cus_Qx7hR3eaEowcv1DX
              user_id: 1
              email: claire.martin@example.com
              revenue: '0.00'
              refunded: '0.00'
              customerRevenue: '0.00'
              customerRefund: '0.00'
              totalTransactionsSuccess: 0
              subTransactionsSuccess: 0
              countTransactions: 1
              customization_products:
              - id: 1
                quantity: 1
              retry_link: null
              is_auto: false
              interval: month
              interval_count: 1
              switch_plan_uuid: null
              switch_plan_date: null
              referrer: newsletter
              display_interval: Monthly
              display_content: Box découverte
              external_fingerprint: null
              content:
              - external_id: '1042'
                quantity: 1
                product_id: 1
                subscription_id: 1
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - import CSV par tâche planifiée
      - import CSV par commande console
  updated_subscription:
    post:
      summary: Un abonnement existant est modifié.
      description: |-
        **Quand il part** : annulation, réactivation (avec ou sans prélèvement immédiat), mise en pause par l'équipe, restauration d'un abonnement supprimé, report anti-churn, changement d'adresse, de transporteur, de déclinaison, de relais ou de personnalisation, mise à jour par l'API.

        **Quand il ne part pas** : bascule de formule programmée (le changement de formule ne notifie rien), report de date de facturation fait par l'équipe, ajustement d'intervalle, application d'un coupon sur la prochaine échéance, écriture de l'empreinte PrestaShop, désactivation en masse à la suppression d'un site.

        **Cascade** : annulation avec fermeture des commandes en attente (`updated_checkoutorder`) ; réactivation avec prélèvement immédiat (`created_checkoutorder`, `created_checkouttransaction`, parfois `created_checkoutinvoice`) ; mise en pause avec fermeture des commandes (`updated_checkoutorder` sur les commandes en attente et en cours de création) ; changement d'adresse, de transporteur, de déclinaison, de relais ou de personnalisation (`updated_shippingbox` par expédition planifiée depuis le premier jour du mois M-2, `updated_checkoutorder` sur les commandes en cours de création) ; report anti-churn (`created_shippingbox` par mois reporté) ; changement de choix de produits (`updated_change_choices`, si activé pour la boutique).
      operationId: updatedSubscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Subscription'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              id: 1
              uuid: sub_N8PxGUEdS2U7DP
              plan:
                id: 1
                uuid: boutique-exemple-box-mensuelle-1
                name: Box mensuelle
                short_name: Mensuelle
                more: Une sélection de trois thés chaque mois
                image: https://s3.eu-central-1.amazonaws.com/ciklik-media/plans/box-mensuelle.jpg
                start_at: null
                price: 29.9
                tax: 0.2
                interval: month
                interval_count: 1
                shipped_count: 1
                position: 1
                engaged_interval: null
                engaged: false
                active: true
              address:
                id: 1
                user_id: 1
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              end_date: '2026-11-09T00:00:00.000000Z'
              engaged_date: null
              auto_pause_at: null
              start_date: '2026-06-10T00:00:00.000000Z'
              active: true
              paused: false
              next_billing: '2026-11-10T08:15:42.000000Z'
              update_transporter_at: null
              relay: null
              graceMonths: null
              user_uuid: cus_Qx7hR3eaEowcv1DX
              user_id: 1
              email: claire.martin-dupont@example.com
              revenue: '31.81'
              refunded: '0.00'
              customerRevenue: '31.81'
              customerRefund: '0.00'
              totalTransactionsSuccess: 1
              subTransactionsSuccess: 1
              countTransactions: 1
              customization_products:
              - id: 1
                quantity: 1
              retry_link: null
              is_auto: false
              interval: month
              interval_count: 1
              switch_plan_uuid: null
              switch_plan_date: null
              referrer: newsletter
              display_interval: Monthly
              display_content: Box découverte
              external_fingerprint: null
              content:
              - external_id: '1042'
                quantity: 1
                product_id: 1
                subscription_id: 1
              changed:
                next_billing: '2026-11-10 08:15:42'
                end_date: '2026-11-09 21:59:59'
                updated_at: '2026-09-10T08:15:42.000000Z'
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade:
      - updated_checkoutorder
      - created_checkoutorder
      - created_checkouttransaction
      - created_checkoutinvoice
      - updated_shippingbox
      - created_shippingbox
      - updated_change_choices
      x-ciklik-not-triggered-by:
      - bascule de formule programmée
      - report de date de facturation fait par l'équipe
      - ajustement d'intervalle
      - application d'un coupon sur la prochaine échéance
      - écriture de l'empreinte PrestaShop
      - désactivation en masse à la suppression d'un site
  deleted_subscription:
    post:
      summary: Un abonnement est supprimé.
      description: |-
        **Quand il part** : suppression par l'API ou par l'équipe, dans les deux cas seulement sur un abonnement inactif.

        **Quand il ne part pas** : la restauration ou la suppression définitive d'un abonnement sont interdites depuis l'interface de l'équipe.

        **Cascade** : les commandes en attente de l'abonnement passent en échec avant l'envoi (`updated_checkoutorder`).
      operationId: deletedSubscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Subscription'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnDelete'
                required:
                - changed
            example:
              id: 1
              uuid: sub_N8PxGUEdS2U7DP
              plan:
                id: 1
                uuid: boutique-exemple-box-mensuelle-1
                name: Box mensuelle
                short_name: Mensuelle
                more: Une sélection de trois thés chaque mois
                image: https://s3.eu-central-1.amazonaws.com/ciklik-media/plans/box-mensuelle.jpg
                start_at: null
                price: 29.9
                tax: 0.2
                interval: month
                interval_count: 1
                shipped_count: 1
                position: 1
                engaged_interval: null
                engaged: false
                active: true
              address:
                id: 1
                user_id: 1
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              end_date: '2026-11-09T00:00:00.000000Z'
              engaged_date: null
              auto_pause_at: null
              start_date: '2026-06-10T00:00:00.000000Z'
              active: true
              paused: false
              next_billing: '2026-11-10T08:15:42.000000Z'
              update_transporter_at: null
              relay: null
              graceMonths: null
              user_uuid: cus_Qx7hR3eaEowcv1DX
              user_id: 1
              email: claire.martin-dupont@example.com
              revenue: '31.81'
              refunded: '0.00'
              customerRevenue: '31.81'
              customerRefund: '0.00'
              totalTransactionsSuccess: 1
              subTransactionsSuccess: 1
              countTransactions: 1
              customization_products:
              - id: 1
                quantity: 2
              retry_link: null
              is_auto: false
              interval: month
              interval_count: 1
              switch_plan_uuid: null
              switch_plan_date: null
              referrer: newsletter
              display_interval: Monthly
              display_content: Box découverte
              external_fingerprint: null
              content:
              - external_id: '1042'
                quantity: 1
                product_id: 1
                subscription_id: 1
              changed:
                deleted: true
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade:
      - updated_checkoutorder
      x-ciklik-not-triggered-by:
      - restauration ou suppression définitive depuis l'interface de l'équipe (interdites)
  created_checkoutorder:
    post:
      summary: Une commande est créée.
      description: |-
        **Quand il part** : passage d'une commande, envoyé 10 secondes après la création. `changed` est vide.

        **Quand il ne part pas** : aucun cas connu.

        **Cascade** : aucune à la création.
      operationId: createdCheckoutorder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Order'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              order_id: 1
              user_id: 1
              user_uuid: cus_Qx7hR3eaEowcv1DX
              first_name: Claire
              last_name: Martin
              phone: '+33612345678'
              email: claire.martin@example.com
              status: pending
              created: '2026-09-10T08:15:42.000000Z'
              items:
              - id: 1
                price: 29.9
                tax: '0.200'
                quantity: 1
                name: Box découverte
                type: App\Product
                orderable:
                  id: 1
                  name: Box découverte
                  tenant_id: 1
                  id_category: null
                  quantity: 250
                  active: true
                  description_short: Trois thés à découvrir
                  description: Trois thés à découvrir chaque mois.
                  slug: box-decouverte
                  meta_description: Box découverte de thés
                  meta_title: Box découverte
                  details: Sachets de 50 g.
                  virtual_product: false
                  salable: true
                  default_variation: null
                  created_at: '2026-09-10T08:15:42.000000Z'
                  updated_at: '2026-09-10T08:15:42.000000Z'
                  deleted_at: null
                  order: 1
                  rating_cache: 0
                  rating_count: 0
                  affilae: null
                  tax: 0.2
                  price: '29.900'
                  gift_card: false
                  gift_plan: null
                  featured: false
                  disable_seo: false
                  old_price: null
                  exodus_id: '1042'
                  details_json: null
                  ref: BOX-DEC-001
                  features:
                    kcal: 0
                  force_shipment: false
                  composable: false
                  selectable_quantities: null
                  metadata: []
                  customizations: []
                affilae: null
                external_id: '1042'
                ref: BOX-DEC-001
              shipping: '4.90'
              count: 1
              total_tax_paid: 5,32
              total_discount_inc: 2,99
              total_paid: 31,81
              total_shipping_paid: 4,90
              created_at: '2026-09-10T08:15:42.000000Z'
              coupon:
                id: 1
                uuid: BIENVENUE10
                plans: null
                price: null
                percent_off: '0.10'
                duration: once
                redeem_by: 1798757999
                display_amount: null
                display_percent: 10%
              subscription_uuid: sub_N8PxGUEdS2U7DP
              revenue: '0.00'
              refunded: '0.00'
              customerRevenue: '0.00'
              customerRefund: '0.00'
              totalTransactionsSuccess: 0
              subTransactionsSuccess: 0
              next_billing: '2026-10-10T08:15:42.000000Z'
              countTransactions: 1
              choices: null
              retry_link: https://boutique.example.com/orders/1/users/1?token=tok_iDXOKjsWGKGwc94K0WptRORT
              paid_transaction_id: null
              paid_class_key: null
              prestashop_order_id: 1587
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 10
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_checkoutorder:
    post:
      summary: Une commande existante est modifiée.
      description: |-
        **Quand il part** : changement de statut (paiement, échec, traitement), recalcul des frais de port lors d'un changement d'adresse ou de transporteur sur l'abonnement lié, fermeture d'une commande en attente ou en cours de création à l'annulation ou à la mise en pause d'un abonnement. Envoyé sans délai : peut arriver avant le `created_checkoutorder` de la même commande.

        **Quand il ne part pas** : passage en demande de rétractation, retour à terminé après validation d'un retrait, liaison de la facture à la commande, marquage d'une relance, pose de la source de paiement.

        **Cascade** : reçu en réaction à un changement sur l'abonnement ou sur l'adresse liée.
      operationId: updatedCheckoutorder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Order'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              order_id: 1
              user_id: 1
              user_uuid: cus_Qx7hR3eaEowcv1DX
              first_name: Claire
              last_name: Martin
              phone: '+33612345678'
              email: claire.martin-dupont@example.com
              status: completed
              created: '2026-09-10T08:15:42.000000Z'
              items:
              - id: 1
                price: 29.9
                tax: '0.200'
                quantity: 1
                name: Box découverte
                type: App\Product
                orderable:
                  id: 1
                  name: Box découverte
                  tenant_id: 1
                  id_category: null
                  quantity: 250
                  active: true
                  description_short: Trois thés à découvrir
                  description: Trois thés à découvrir chaque mois.
                  slug: box-decouverte
                  meta_description: Box découverte de thés
                  meta_title: Box découverte
                  details: Sachets de 50 g.
                  virtual_product: false
                  salable: true
                  default_variation: null
                  created_at: '2026-09-10T08:15:42.000000Z'
                  updated_at: '2026-09-10T08:15:42.000000Z'
                  deleted_at: null
                  order: 1
                  rating_cache: 0
                  rating_count: 0
                  affilae: null
                  tax: 0.2
                  price: '29.900'
                  gift_card: false
                  gift_plan: null
                  featured: false
                  disable_seo: false
                  old_price: null
                  exodus_id: '1042'
                  details_json: null
                  ref: BOX-DEC-001
                  features:
                    kcal: 0
                  force_shipment: false
                  composable: false
                  selectable_quantities: null
                  metadata: []
                  customizations: []
                affilae: null
                external_id: '1042'
                ref: BOX-DEC-001
              shipping: '4.90'
              count: 1
              total_tax_paid: 5,32
              total_discount_inc: 2,99
              total_paid: 31,81
              total_shipping_paid: 4,90
              created_at: '2026-09-10T08:15:42.000000Z'
              coupon:
                id: 1
                uuid: BIENVENUE10
                plans: null
                price: null
                percent_off: '0.10'
                duration: once
                redeem_by: 1798757999
                display_amount: null
                display_percent: 10%
              subscription_uuid: sub_N8PxGUEdS2U7DP
              revenue: '31.81'
              refunded: '0.00'
              customerRevenue: '31.81'
              customerRefund: '0.00'
              totalTransactionsSuccess: 1
              subTransactionsSuccess: 1
              next_billing: '2026-10-10T08:15:42.000000Z'
              countTransactions: 1
              choices: null
              retry_link: https://boutique.example.com/orders/1/users/1?token=tok_iDXOKjsWGKGwc94K0WptRORT
              paid_transaction_id: ch_3QxK2mL9aBcDeFgH1a2b3c4d
              paid_class_key: stripe
              prestashop_order_id: 1587
              changed:
                status: completed
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - passage en demande de rétractation
      - retour à terminé après validation d'un retrait
      - liaison de la facture à la commande
      - marquage d'une relance
      - pose de la source de paiement
  created_checkouttransaction:
    post:
      summary: Une transaction de paiement est créée.
      description: |-
        **Quand il part** : tentative de paiement d'une commande, envoyé 10 secondes après validation de la transaction SQL englobante.

        **Quand il ne part pas** : aucun cas connu.

        **Cascade** : aucune à la création.
      operationId: createdCheckouttransaction
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Transaction'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              order_id: 1
              order:
                order_id: 1
                user_id: 1
                user_uuid: cus_Qx7hR3eaEowcv1DX
                first_name: Claire
                last_name: Martin
                phone: '+33612345678'
                email: claire.martin@example.com
                status: pending
                created: '2026-09-10T08:15:42.000000Z'
                items:
                - id: 1
                  price: 29.9
                  tax: '0.200'
                  quantity: 1
                  name: Box découverte
                  type: App\Product
                  orderable:
                    id: 1
                    name: Box découverte
                    tenant_id: 1
                    id_category: null
                    quantity: 250
                    active: true
                    description_short: Trois thés à découvrir
                    description: Trois thés à découvrir chaque mois.
                    slug: box-decouverte
                    meta_description: Box découverte de thés
                    meta_title: Box découverte
                    details: Sachets de 50 g.
                    virtual_product: false
                    salable: true
                    default_variation: null
                    created_at: '2026-09-10T08:15:42.000000Z'
                    updated_at: '2026-09-10T08:15:42.000000Z'
                    deleted_at: null
                    order: 1
                    rating_cache: 0
                    rating_count: 0
                    affilae: null
                    tax: 0.2
                    price: '29.900'
                    gift_card: false
                    gift_plan: null
                    featured: false
                    disable_seo: false
                    old_price: null
                    exodus_id: '1042'
                    details_json: null
                    ref: BOX-DEC-001
                    features:
                      kcal: 0
                    force_shipment: false
                    composable: false
                    selectable_quantities: null
                    metadata: []
                    customizations: []
                  affilae: null
                  external_id: '1042'
                  ref: BOX-DEC-001
                shipping: '4.90'
                count: 1
                total_tax_paid: 5,32
                total_discount_inc: 2,99
                total_paid: 31,81
                total_shipping_paid: 4,90
                created_at: '2026-09-10T08:15:42.000000Z'
                coupon:
                  id: 1
                  uuid: BIENVENUE10
                  plans: null
                  price: null
                  percent_off: '0.10'
                  duration: once
                  redeem_by: 1798757999
                  display_amount: null
                  display_percent: 10%
                subscription_uuid: sub_N8PxGUEdS2U7DP
                revenue: '0.00'
                refunded: '0.00'
                customerRevenue: '0.00'
                customerRefund: '0.00'
                totalTransactionsSuccess: 0
                subTransactionsSuccess: 0
                next_billing: '2026-10-10T08:15:42.000000Z'
                countTransactions: 1
                choices: null
                retry_link: https://boutique.example.com/orders/1/users/1?token=tok_iDXOKjsWGKGwc94K0WptRORT
                paid_transaction_id: null
                paid_class_key: null
                prestashop_order_id: 1587
              gateway: stripe
              transaction_id: ch_3QxK2mL9aBcDeFgH1a2b3c4d
              detail: null
              token: null
              tenant_id: 1
              paid: false
              amount: '31.81'
              refunded: false
              source_id: pm_1QxK2mL9aBcDeFgH
              failure_message: null
              failure_code: null
              invoice_id: null
              amount_refunded: '0.00'
              statement_descriptor: BOUTIQUE EXEMPLE
              user_id: 1
              created_at: '2026-09-10T08:15:42.000000Z'
              updated_at: '2026-09-10T08:15:42.000000Z'
              payment_intent_id: pi_3QxK2mL9aBcDeFgH1a2b3c4d
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 10
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_checkouttransaction:
    post:
      summary: Une transaction de paiement est modifiée.
      description: |-
        **Quand il part** : passage au paiement effectif (`changed.paid`), envoyé 10 secondes après l'écriture.

        **Quand il ne part pas** : liaison de la facture à la transaction.

        **Cascade** : aucune.
      operationId: updatedCheckouttransaction
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Transaction'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              order_id: 1
              order:
                order_id: 1
                user_id: 1
                user_uuid: cus_Qx7hR3eaEowcv1DX
                first_name: Claire
                last_name: Martin
                phone: '+33612345678'
                email: claire.martin-dupont@example.com
                status: pending
                created: '2026-09-10T08:15:42.000000Z'
                items:
                - id: 1
                  price: 29.9
                  tax: '0.200'
                  quantity: 1
                  name: Box découverte
                  type: App\Product
                  orderable:
                    id: 1
                    name: Box découverte
                    tenant_id: 1
                    id_category: null
                    quantity: 250
                    active: true
                    description_short: Trois thés à découvrir
                    description: Trois thés à découvrir chaque mois.
                    slug: box-decouverte
                    meta_description: Box découverte de thés
                    meta_title: Box découverte
                    details: Sachets de 50 g.
                    virtual_product: false
                    salable: true
                    default_variation: null
                    created_at: '2026-09-10T08:15:42.000000Z'
                    updated_at: '2026-09-10T08:15:42.000000Z'
                    deleted_at: null
                    order: 1
                    rating_cache: 0
                    rating_count: 0
                    affilae: null
                    tax: 0.2
                    price: '29.900'
                    gift_card: false
                    gift_plan: null
                    featured: false
                    disable_seo: false
                    old_price: null
                    exodus_id: '1042'
                    details_json: null
                    ref: BOX-DEC-001
                    features:
                      kcal: 0
                    force_shipment: false
                    composable: false
                    selectable_quantities: null
                    metadata: []
                    customizations: []
                  affilae: null
                  external_id: '1042'
                  ref: BOX-DEC-001
                shipping: '4.90'
                count: 1
                total_tax_paid: 5,32
                total_discount_inc: 2,99
                total_paid: 31,81
                total_shipping_paid: 4,90
                created_at: '2026-09-10T08:15:42.000000Z'
                coupon:
                  id: 1
                  uuid: BIENVENUE10
                  plans: null
                  price: null
                  percent_off: '0.10'
                  duration: once
                  redeem_by: 1798757999
                  display_amount: null
                  display_percent: 10%
                subscription_uuid: sub_N8PxGUEdS2U7DP
                revenue: '0.00'
                refunded: '0.00'
                customerRevenue: '31.81'
                customerRefund: '0.00'
                totalTransactionsSuccess: 1
                subTransactionsSuccess: 0
                next_billing: '2026-10-10T08:15:42.000000Z'
                countTransactions: 1
                choices: null
                retry_link: https://boutique.example.com/orders/1/users/1?token=tok_iDXOKjsWGKGwc94K0WptRORT
                paid_transaction_id: ch_3QxK2mL9aBcDeFgH1a2b3c4d
                paid_class_key: stripe
                prestashop_order_id: 1587
              gateway: stripe
              transaction_id: ch_3QxK2mL9aBcDeFgH1a2b3c4d
              detail: null
              token: null
              tenant_id: 1
              paid: true
              amount: '31.81'
              refunded: false
              source_id: pm_1QxK2mL9aBcDeFgH
              failure_message: null
              failure_code: null
              invoice_id: null
              amount_refunded: '0.00'
              statement_descriptor: BOUTIQUE EXEMPLE
              user_id: 1
              created_at: '2026-09-10T08:15:42.000000Z'
              updated_at: '2026-09-10T08:15:42.000000Z'
              payment_intent_id: pi_3QxK2mL9aBcDeFgH1a2b3c4d
              changed:
                paid: true
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 10
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - liaison de la facture à la transaction
  created_checkoutinvoice:
    post:
      summary: Une facture est émise.
      description: |-
        **Quand il part** : émission d'une facture après paiement, envoyé 15 secondes après l'écriture.

        **Quand il ne part pas** : il n'existe pas d'événement de modification de facture : aucune facture déjà émise n'est renotifiée.

        **Cascade** : aucune.
      operationId: createdCheckoutinvoice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Invoice'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              order_id: 1
              customer:
                first_name: Claire
                last_name: Martin
                email: claire.martin-dupont@example.com
                address: 27 avenue Jean Jaurès
                postcode: '69007'
                city: Lyon
                country: France
              tenant_id: 1
              emitted_at: '2026-09-10T00:00:00.000000Z'
              company:
                name: Boutique Exemple SAS
                address: 5 place Bellecour
                postcode: '69002'
                city: Lyon
                siret: '12345678900012'
                vat: FR12345678900
              number: 42
              total_tax: '5.320'
              total_discount: '2.990'
              total: '31.810'
              total_shipping: '4.900'
              more: null
              uuid: F2026-09-42
              order:
                order_id: 1
                user_id: 1
                user_uuid: cus_Qx7hR3eaEowcv1DX
                first_name: Claire
                last_name: Martin
                phone: '+33612345678'
                email: claire.martin-dupont@example.com
                status: completed
                created: '2026-09-10T08:15:42.000000Z'
                items:
                - id: 1
                  price: 29.9
                  tax: '0.200'
                  quantity: 1
                  name: Box découverte
                  type: App\Product
                  orderable:
                    id: 1
                    name: Box découverte
                    tenant_id: 1
                    id_category: null
                    quantity: 250
                    active: true
                    description_short: Trois thés à découvrir
                    description: Trois thés à découvrir chaque mois.
                    slug: box-decouverte
                    meta_description: Box découverte de thés
                    meta_title: Box découverte
                    details: Sachets de 50 g.
                    virtual_product: false
                    salable: true
                    default_variation: null
                    created_at: '2026-09-10T08:15:42.000000Z'
                    updated_at: '2026-09-10T08:15:42.000000Z'
                    deleted_at: null
                    order: 1
                    rating_cache: 0
                    rating_count: 0
                    affilae: null
                    tax: 0.2
                    price: '29.900'
                    gift_card: false
                    gift_plan: null
                    featured: false
                    disable_seo: false
                    old_price: null
                    exodus_id: '1042'
                    details_json: null
                    ref: BOX-DEC-001
                    features:
                      kcal: 0
                    force_shipment: false
                    composable: false
                    selectable_quantities: null
                    metadata: []
                    customizations: []
                  affilae: null
                  external_id: '1042'
                  ref: BOX-DEC-001
                shipping: '4.90'
                count: 1
                total_tax_paid: 5,32
                total_discount_inc: 2,99
                total_paid: 31,81
                total_shipping_paid: 4,90
                created_at: '2026-09-10T08:15:42.000000Z'
                coupon:
                  id: 1
                  uuid: BIENVENUE10
                  plans: null
                  price: null
                  percent_off: '0.10'
                  duration: once
                  redeem_by: 1798757999
                  display_amount: null
                  display_percent: 10%
                subscription_uuid: sub_N8PxGUEdS2U7DP
                revenue: '31.81'
                refunded: '0.00'
                customerRevenue: '31.81'
                customerRefund: '0.00'
                totalTransactionsSuccess: 1
                subTransactionsSuccess: 1
                next_billing: '2026-10-10T08:15:42.000000Z'
                countTransactions: 1
                choices: null
                retry_link: https://boutique.example.com/orders/1/users/1?token=tok_iDXOKjsWGKGwc94K0WptRORT
                paid_transaction_id: ch_3QxK2mL9aBcDeFgH1a2b3c4d
                paid_class_key: stripe
                prestashop_order_id: 1587
              gateway: stripe
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 15
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - toute modification d'une facture déjà émise (aucun événement de mise à jour n'existe)
  created_address:
    post:
      summary: Une adresse est créée.
      description: |-
        **Quand il part** : ajout d'une adresse par le client ou par l'API.

        **Quand il ne part pas** : import CSV.

        **Cascade** : aucune à la création.
      operationId: createdAddress
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Address'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              id: 1
              user_id: 1
              address: 12 rue des Lilas
              address1: Bâtiment B, 3e étage
              postcode: '69003'
              city: Lyon
              phone: '+33612345678'
              first_name: Claire
              last_name: Martin
              region_id: 1
              division: null
              division_name: null
              deletable: false
              company_name: null
              country:
                id: 1
                name: France
                alphaCode: FR
              external_id: 4821
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - import CSV
  updated_address:
    post:
      summary: Une adresse existante est modifiée.
      description: |-
        **Quand il part** : toute modification d'une adresse, y compris un champ sans rapport avec la livraison (le code ne teste pas quel champ a changé).

        **Quand il ne part pas** : aucun cas connu.

        **Cascade** : pour chaque abonnement lié à l'adresse, un `updated_shippingbox` par expédition planifiée depuis le premier jour du mois M-2 (expéditions déjà parties incluses), et un ou deux `updated_checkoutorder` par commande en cours de création. Un client à plusieurs abonnements multiplie la cascade.
      operationId: updatedAddress
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Address'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              id: 1
              user_id: 1
              address: 27 avenue Jean Jaurès
              address1: ''
              postcode: '69007'
              city: Lyon
              phone: '+33612345678'
              first_name: Claire
              last_name: Martin
              region_id: 1
              division: null
              division_name: null
              deletable: false
              company_name: null
              country:
                id: 1
                name: France
                alphaCode: FR
              external_id: 4821
              changed:
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-not-triggered-by: []
      x-ciklik-cascade:
      - updated_shippingbox
      - updated_checkoutorder
  created_shippingbox:
    post:
      summary: Une expédition est planifiée.
      description: |-
        **Quand il part** : génération d'une expédition liée à un abonnement ou à une commande, ou report anti-churn (une expédition créée par mois reporté).

        **Quand il ne part pas** : aucun cas connu.

        **Cascade** : aucune à la création.
      operationId: createdShippingbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Shipment'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              tenant_id: 1
              id: 1
              uuid: c636e863-bdf5-4914-b108-05e117cb0c12
              address:
                id: 1
                user_id: 1
                address: 12 rue des Lilas
                address1: Bâtiment B, 3e étage
                postcode: '69003'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              updated_at: '2026-09-10T08:15:42.000000Z'
              scheduled_at: '2026-09-15T00:00:00.000000Z'
              tracking_link: null
              subscription_uuid: sub_N8PxGUEdS2U7DP
              address_id: 1
              phone: '+33612345678'
              status: created
              tracking_number: null
              customer_id: cus_Qx7hR3eaEowcv1DX
              supply_id: null
              transporter_id: 1
              first_name: Claire
              last_name: Martin
              postcode: '69003'
              address1: 12 rue des Lilas
              address2: Bâtiment B, 3e étage
              division: null
              city: Lyon
              country: France
              mail: claire.martin@example.com
              subscriptions_count: '1'
              transporter_name: Colissimo domicile
              shipped_count: '1'
              site_name: Boutique Exemple
              options: null
              iso_code: fr
              relay: null
              relay_id: null
              orderable_name: Box mensuelle
              orderable_id: '1'
              orderable_type: App\Plan
              order_id: 1
              ref: BOX-MENSUELLE
              customization_products:
              - id: 1
                quantity: 1
              extra_customization_products: null
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_shippingbox:
    post:
      summary: Une expédition existante est modifiée.
      description: |-
        **Quand il part** : changement de statut, pose d'un numéro de suivi, synchronisation des champs d'adresse depuis l'abonnement ou le client. Une même modification peut produire deux envois successifs pour la même expédition (synchronisation interne déclenchant une seconde écriture).

        **Quand il ne part pas** : liaison à une commande, ajout de personnalisations supplémentaires, recalcul du compteur d'expéditions. Le passage au statut expédié n'est notifié que lorsqu'il est posé par l'API vendors (`POST /deliveries/{id}`, champ `status` ou `tracking_number`) ou par l'import de numéros de suivi de l'équipe Ciklik ; il n'est **jamais** notifié quand il provient des connecteurs logistiques Wonderweb ou Effitrace.

        **Cascade** : un changement d'adresse, un changement sur l'abonnement (adresse, transporteur, déclinaison, relais, personnalisation) ou un changement d'adresse e-mail du client produisent chacun un `updated_shippingbox` par expédition planifiée depuis le premier jour du mois M-2.
      operationId: updatedShippingbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Shipment'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              tenant_id: 1
              id: 1
              uuid: c636e863-bdf5-4914-b108-05e117cb0c12
              address:
                id: 1
                user_id: 1
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              updated_at: '2026-09-10T08:15:42.000000Z'
              scheduled_at: '2026-09-15T00:00:00.000000Z'
              tracking_link: https://www.laposte.fr/outils/suivre-vos-envois?code=
              subscription_uuid: sub_N8PxGUEdS2U7DP
              address_id: 1
              phone: 06 12 34 56 78
              status: shipped
              tracking_number: 6A12345678901
              customer_id: cus_Qx7hR3eaEowcv1DX
              supply_id: null
              transporter_id: 1
              first_name: CLAIRE
              last_name: MARTIN
              postcode: '69007'
              address1: 27 AVENUE JEAN JAURES
              address2: ''
              division: null
              city: LYON
              country: FRANCE
              mail: claire.martin-dupont@example.com
              subscriptions_count: '1'
              transporter_name: Colissimo domicile
              shipped_count: '1'
              site_name: Boutique Exemple
              options: ''
              iso_code: fr
              relay: null
              relay_id: null
              orderable_name: Box mensuelle
              orderable_id: '1'
              orderable_type: App\Plan
              order_id: 1
              ref: BOX-MENSUELLE
              customization_products:
              - id: 1
                quantity: 2
              extra_customization_products: null
              changed:
                phone: 06 12 34 56 78
                first_name: CLAIRE
                last_name: MARTIN
                postcode: '69007'
                address1: 27 AVENUE JEAN JAURES
                address2: ''
                city: LYON
                country: FRANCE
                mail: claire.martin-dupont@example.com
                status: shipped
                tracking_number: 6A12345678901
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - liaison à une commande
      - ajout de personnalisations supplémentaires
      - recalcul du compteur d'expéditions
      - passage au statut expédié via les connecteurs logistiques Wonderweb ou Effitrace
  deleted_shippingbox:
    post:
      summary: Une expédition est supprimée.
      description: |-
        **Quand il part** : suppression par l'API, validation d'un retrait avec suppression des expéditions futures, remboursement avec suppression des expéditions de la commande.

        **Quand il ne part pas** : la mise en pause d'un abonnement avec suppression des expéditions futures ne notifie rien.

        **Cascade** : aucune.
      operationId: deletedShippingbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Shipment'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnDelete'
                required:
                - changed
            example:
              tenant_id: 1
              id: 1
              uuid: c636e863-bdf5-4914-b108-05e117cb0c12
              address:
                id: 1
                user_id: 1
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              updated_at: '2026-09-10T08:15:42.000000Z'
              scheduled_at: '2026-09-15T00:00:00.000000Z'
              tracking_link: https://www.laposte.fr/outils/suivre-vos-envois?code=
              subscription_uuid: sub_N8PxGUEdS2U7DP
              address_id: 1
              phone: 06 12 34 56 78
              status: shipped
              tracking_number: 6A12345678901
              customer_id: cus_Qx7hR3eaEowcv1DX
              supply_id: null
              transporter_id: 1
              first_name: CLAIRE
              last_name: MARTIN
              postcode: '69007'
              address1: 27 AVENUE JEAN JAURES
              address2: ''
              division: null
              city: LYON
              country: FRANCE
              mail: claire.martin-dupont@example.com
              subscriptions_count: '1'
              transporter_name: Colissimo domicile
              shipped_count: '1'
              site_name: Boutique Exemple
              options: ''
              iso_code: fr
              relay: null
              relay_id: null
              orderable_name: Box mensuelle
              orderable_id: '1'
              orderable_type: App\Plan
              order_id: 1
              ref: BOX-MENSUELLE
              customization_products:
              - id: 1
                quantity: 2
              extra_customization_products: null
              changed:
                deleted: true
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by:
      - mise en pause d'un abonnement avec suppression des expéditions futures
  created_user:
    post:
      summary: Un compte client est créé.
      description: |-
        **Quand il part** : inscription, passage d'une commande, création par l'API, validation d'un panier PrestaShop.

        **Quand il ne part pas** : aucun cas connu côté marchand.

        **Cascade** : aucune à la création.
      operationId: createdUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/User'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              id: 1
              email: claire.martin@example.com
              uuid: cus_Qx7hR3eaEowcv1DX
              refp: https://boutique.example.com/formules?refp=CLAIRE2026
              has_active_subscription: false
              info: null
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_user:
    post:
      summary: Un compte client existant est modifié.
      description: |-
        **Quand il part** : beaucoup plus souvent qu'on ne le croit, y compris sur des changements purement techniques liés à la session ou à la sécurité du compte, sans qu'aucune donnée métier n'ait changé (connexion avec mémorisation, déconnexion, activation ou modification de l'authentification à deux facteurs, réinitialisation de mot de passe). Ne déclenchez une action métier que si un champ que vous exploitez a réellement changé.

        **Quand il ne part pas** : ajout du parrain juste après l'inscription, import CSV, rattachement de l'empreinte PrestaShop.

        **Cascade** : un changement d'adresse e-mail produit, en plus de cet envoi, un `updated_shippingbox` par expédition planifiée depuis le premier jour du mois M-2.
      operationId: updatedUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/User'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              id: 1
              email: claire.martin-dupont@example.com
              uuid: cus_Qx7hR3eaEowcv1DX
              refp: https://boutique.example.com/formules?refp=CLAIRE2026
              has_active_subscription: true
              info: null
              changed:
                email: claire.martin-dupont@example.com
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade:
      - updated_shippingbox
      x-ciklik-not-triggered-by:
      - ajout du parrain juste après l'inscription
      - import CSV
      - rattachement de l'empreinte PrestaShop
  created_optin:
    post:
      summary: Une inscription marketing est créée.
      description: |-
        **Quand il part** : première inscription d'un couple adresse e-mail et boutique, à l'inscription ou à la création, l'annulation ou la réactivation d'un abonnement. Envoyé 10 secondes après l'écriture.

        **Quand il ne part pas** : une réinscription pour un couple déjà connu, qui produit un `updated_optin`.

        **Cascade** : aucune.
      operationId: createdOptin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Optin'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnCreate'
                required:
                - changed
            example:
              email: claire.martin@example.com
              tenant_id: 1
              valid: true
              list_id: 9f2c4e7a1b
              changed: []
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 10
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_optin:
    post:
      summary: Une inscription marketing existante est modifiée.
      description: |-
        **Quand il part** : changement de liste, désinscription, rejet définitif par le fournisseur d'e-mail, désinscription en masse faite par l'équipe.

        **Quand il ne part pas** : aucun cas connu.

        **Cascade** : aucune.
      operationId: updatedOptin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Optin'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              email: claire.martin@example.com
              tenant_id: 1
              valid: false
              list_id: 9f2c4e7a1b
              changed:
                valid: false
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: enum
      x-ciklik-cascade: []
      x-ciklik-not-triggered-by: []
  updated_change_choices:
    post:
      summary: Le client change ses choix de produits sur un abonnement.
      description: |-
        **Quand il part** : émis en plus de `updated_subscription` quand le champ de personnalisation des produits d'un abonnement change.

        **Quand il ne part pas** : cette clé n'existe pas dans la liste validée par l'API vendors : une tentative de `POST /webhooks` avec cette valeur renvoie une erreur 422. Elle ne peut être activée que par l'équipe Ciklik.

        **Cascade** : toujours accompagné d'un `updated_subscription` pour le même abonnement.
      operationId: updatedChangeChoices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/Subscription'
              - type: object
                properties:
                  changed:
                    $ref: '#/components/schemas/ChangedOnUpdate'
                required:
                - changed
            example:
              id: 1
              uuid: sub_N8PxGUEdS2U7DP
              plan:
                id: 1
                uuid: boutique-exemple-box-mensuelle-1
                name: Box mensuelle
                short_name: Mensuelle
                more: Une sélection de trois thés chaque mois
                image: https://s3.eu-central-1.amazonaws.com/ciklik-media/plans/box-mensuelle.jpg
                start_at: null
                price: 29.9
                tax: 0.2
                interval: month
                interval_count: 1
                shipped_count: 1
                position: 1
                engaged_interval: null
                engaged: false
                active: true
              address:
                id: 1
                user_id: 1
                address: 27 avenue Jean Jaurès
                address1: ''
                postcode: '69007'
                city: Lyon
                phone: '+33612345678'
                first_name: Claire
                last_name: Martin
                region_id: 1
                division: null
                division_name: null
                deletable: false
                company_name: null
                country:
                  id: 1
                  name: France
                  alphaCode: FR
                external_id: 4821
              transporter:
                id: 1
                countries:
                - id: 1
                  name: France
                plan_blacklisted: []
                name: Colissimo domicile
                description: Livraison à domicile sous 48h
                price: 4.9
                gift: false
                active: true
                shipped_count: 1
                type: subs
                relayOptions: null
              created_at: '2026-09-10T08:15:42.000000Z'
              end_date: '2026-11-09T00:00:00.000000Z'
              engaged_date: null
              auto_pause_at: null
              start_date: '2026-06-10T00:00:00.000000Z'
              active: true
              paused: false
              next_billing: '2026-11-10T08:15:42.000000Z'
              update_transporter_at: null
              relay: null
              graceMonths: null
              user_uuid: cus_Qx7hR3eaEowcv1DX
              user_id: 1
              email: claire.martin-dupont@example.com
              revenue: '31.81'
              refunded: '0.00'
              customerRevenue: '31.81'
              customerRefund: '0.00'
              totalTransactionsSuccess: 1
              subTransactionsSuccess: 1
              countTransactions: 1
              customization_products:
              - id: 1
                quantity: 2
              retry_link: null
              is_auto: false
              interval: month
              interval_count: 1
              switch_plan_uuid: null
              switch_plan_date: null
              referrer: newsletter
              display_interval: Monthly
              display_content: Box découverte
              external_fingerprint: null
              content:
              - external_id: '1042'
                quantity: 1
                product_id: 1
                subscription_id: 1
              changed:
                customization_products: '[{"id":1,"quantity":2}]'
      responses:
        2XX:
          description: 'Réponse attendue, reçue dans les 30 secondes. Tout autre code HTTP est cependant
            accepté sans déclencher de nouvel essai : seule une erreur de connexion (DNS, refus, délai,
            certificat invalide) peut entraîner un renvoi.'
      security: []
      x-ciklik-delay-seconds: 0
      x-ciklik-retries:
        max: 5
        spacing-seconds: 30
        only-on: connection-error
        duplicate-on-slow-response: true
      x-ciklik-availability: on-request
      x-ciklik-not-triggered-by: []
      x-ciklik-cascade:
      - updated_subscription
paths:
  /webhooks:
    post:
      summary: Déclarer une URL de réception pour un événement
      description: 'Enregistre l''URL qui recevra les envois de la clé d''événement donnée. Un second
        appel sur la même clé remplace l''URL précédente sans avertissement. La nouvelle valeur est prise
        en compte au bout d''une minute au plus. La validation porte sur la syntaxe de l''URL seulement
        : `http://` est accepté, une URL inaccessible aussi, aucun appel de test n''est fait. Utilisez
        HTTPS et vérifiez vous-même que l''adresse répond. L''en-tête `Accept: application/json` est
        obligatoire : sans lui, les erreurs 401 et 422 sont renvoyées sous forme de redirection HTML.'
      operationId: registerWebhook
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/AcceptJson'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - hook_url
              - event_type
              properties:
                hook_url:
                  type: string
                  format: uri
                  description: URL de réception. Syntaxe validée seulement, HTTPS non imposé.
                event_type:
                  $ref: '#/components/schemas/WebhookEventType'
      responses:
        '200':
          description: URL enregistrée. La réponse contient l'objet complet des URL déclarées.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigurationResponse'
        '401':
          description: 'Jeton absent ou invalide. Sans l''en-tête `Accept: application/json`, cette erreur
            est renvoyée sous forme de redirection HTML plutôt que de réponse JSON.'
        '422':
          description: 'Paramètre invalide ou clé d''événement inconnue. Sans l''en-tête `Accept: application/json`,
            cette erreur est renvoyée sous forme de redirection HTML plutôt que de réponse JSON.'
        '429':
          description: 'Quota dépassé : 100 requêtes par minute et par adresse IP, non par jeton.'
    delete:
      summary: Supprimer l'URL de réception déclarée pour un événement
      description: Retire l'URL associée à la clé d'événement donnée. La réponse contient l'objet complet
        des URL restant déclarées.
      operationId: removeWebhook
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/AcceptJson'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - event_type
              properties:
                event_type:
                  $ref: '#/components/schemas/WebhookEventType'
      responses:
        '200':
          description: URL supprimée. La réponse contient l'objet complet des URL restant déclarées.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigurationResponse'
        '401':
          description: 'Jeton absent ou invalide. Sans l''en-tête `Accept: application/json`, cette erreur
            est renvoyée sous forme de redirection HTML plutôt que de réponse JSON.'
        '422':
          description: 'Paramètre invalide ou clé d''événement inconnue. Sans l''en-tête `Accept: application/json`,
            cette erreur est renvoyée sous forme de redirection HTML plutôt que de réponse JSON.'
        '429':
          description: 'Quota dépassé : 100 requêtes par minute et par adresse IP, non par jeton.'
    get:
      deprecated: true
      summary: Lister les derniers envois d'un événement (dépréciée)
      description: 'Cette opération ne renvoie aucun résultat exploitable dans l''état actuel du service
        : évitez de vous en servir pour vérifier une configuration.'
      operationId: listWebhookDeliveries
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/AcceptJson'
      - name: event_type
        in: query
        required: false
        schema:
          type: string
        description: Clé d'événement recherchée, non validée par le serveur.
      responses:
        '200':
          description: Liste des derniers envois trouvés pour cette clé, généralement vide. La réponse
            est enveloppée dans une clé `data` (comportement par défaut de la ressource Laravel).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: {}
        '401':
          description: 'Jeton absent ou invalide. Sans l''en-tête `Accept: application/json`, cette erreur
            est renvoyée sous forme de redirection HTML plutôt que de réponse JSON.'
        '404':
          description: Clé d'événement inconnue.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  parameters:
    AcceptJson:
      name: Accept
      in: header
      required: true
      schema:
        type: string
        const: application/json
      description: 'Obligatoire : sans cet en-tête, les erreurs 401 et 422 sont renvoyées sous forme
        de redirection HTML plutôt que de réponse JSON.'
  schemas:
    Country:
      type: object
      description: Pays de livraison ou de facturation.
      required:
      - id
      - name
      - alphaCode
      properties:
        id:
          type: integer
          description: Identifiant interne du pays.
        name:
          type: string
          description: Nom du pays en français.
        alphaCode:
          type: string
          description: Code pays ISO à deux lettres.
    Address:
      type: object
      description: Adresse d'un client.
      required:
      - id
      - user_id
      - address
      - address1
      - postcode
      - city
      - phone
      - first_name
      - last_name
      - region_id
      - division
      - division_name
      - deletable
      - company_name
      - country
      - external_id
      properties:
        id:
          type: integer
          description: Identifiant interne de l'adresse.
        user_id:
          type: integer
          description: Identifiant interne du client.
        address:
          type: string
          description: Ligne d'adresse principale.
        address1:
          type:
          - string
          - 'null'
          description: Complément d'adresse.
        postcode:
          type: string
          description: Code postal.
        city:
          type: string
          description: Ville.
        phone:
          type:
          - string
          - 'null'
          description: Téléphone au format E.164 si analysable, sinon valeur brute.
        first_name:
          type: string
          description: Prénom du destinataire.
        last_name:
          type: string
          description: Nom du destinataire.
        region_id:
          type: integer
          description: Identifiant interne de la région.
        division:
          type:
          - string
          - 'null'
          description: Subdivision administrative (état, province).
        division_name:
          type:
          - string
          - 'null'
          description: Nom lisible de la subdivision.
        deletable:
          type: boolean
          description: 'Résultat d''un calcul de droits au moment de l''envoi, pas un état stable : faux
            dès que le client n''a qu''une adresse ou qu''elle est liée à un abonnement, une expédition
            ou une commande. Ne pas s''en servir comme donnée métier.'
        company_name:
          type:
          - string
          - 'null'
          description: Raison sociale, pour une adresse professionnelle.
        country:
          $ref: '#/components/schemas/Country'
        external_id:
          type:
          - integer
          - 'null'
          description: Identifiant d'origine (import Exodus).
    Plan:
      type: object
      description: Formule d'abonnement.
      required:
      - id
      - uuid
      - name
      - short_name
      - more
      - image
      - start_at
      - price
      - tax
      - interval
      - interval_count
      - shipped_count
      - position
      - engaged_interval
      - engaged
      - active
      properties:
        id:
          type: integer
          description: Identifiant interne de la formule.
        uuid:
          type: string
          description: Identifiant public de la formule.
        name:
          type: string
          description: Nom de la formule.
        short_name:
          type:
          - string
          - 'null'
          description: Nom court de la formule.
        more:
          type:
          - string
          - 'null'
          description: Description complémentaire.
        image:
          type: string
          description: URL de l'image de la formule.
        start_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de début de disponibilité de la formule.
        price:
          type: number
          description: Prix de la formule, hors devise explicite.
        tax:
          type: number
          description: Taux de TVA appliqué, en fraction (0.2 pour 20 %).
        interval:
          type: string
          description: Unité de périodicité (`month`, `week`...).
        interval_count:
          type: integer
          description: Nombre d'unités entre deux échéances.
        shipped_count:
          type: integer
          description: Nombre d'expéditions déjà livrées sur cette formule.
        position:
          type:
          - integer
          - 'null'
          description: Position d'affichage de la formule.
        engaged_interval:
          type:
          - string
          - 'null'
          description: Durée d'engagement, si la formule en impose une.
        engaged:
          type: boolean
          description: Vrai si la formule impose un engagement.
        active:
          type: boolean
          description: Vrai si la formule est active.
    Transporter:
      type: object
      description: Transporteur associé à un abonnement ou à une expédition.
      required:
      - id
      - countries
      - plan_blacklisted
      - name
      - description
      - price
      - gift
      - active
      - shipped_count
      - type
      - relayOptions
      properties:
        id:
          type: integer
          description: Identifiant interne du transporteur.
        countries:
          type: array
          description: Pays desservis.
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        plan_blacklisted:
          type: array
          description: Formules exclues de ce transporteur.
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        name:
          type: string
          description: Nom du transporteur.
        description:
          type: string
          description: Description commerciale du transporteur.
        price:
          type: number
          description: Prix du transport.
        gift:
          type: boolean
          description: Vrai si le transporteur est proposé pour les cartes cadeaux.
        active:
          type: boolean
          description: Vrai si le transporteur est actif.
        shipped_count:
          type: integer
          description: Nombre d'expéditions déjà confiées à ce transporteur.
        type:
          type: string
          description: Catégorie interne du transporteur.
        relayOptions:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: 'Contenu variable selon le transporteur relais choisi, non contractuel, à ignorer.
            Structure non documentée : ne construisez aucune intégration sur son contenu.'
    Product:
      type:
      - object
      - 'null'
      additionalProperties: true
      description: 'Reflet interne du produit ou de la formule à l''origine de la ligne de commande, structure
        non contractuelle. N''exploitez que `id`, `name`, `price` et `ref` : le reste peut changer sans
        préavis.'
      required:
      - id
      - name
      - price
      - ref
      properties:
        id:
          type: integer
          description: Identifiant interne du produit ou de la formule.
        name:
          type: string
          description: Nom du produit ou de la formule.
        price:
          type: string
          description: Prix stocké, en chaîne décimale.
        ref:
          type:
          - string
          - 'null'
          description: Référence marchande du produit.
    Coupon:
      type: object
      description: Coupon appliqué à une commande.
      required:
      - id
      - uuid
      - plans
      - price
      - percent_off
      - duration
      - redeem_by
      - display_amount
      - display_percent
      properties:
        id:
          type: integer
          description: Identifiant interne du coupon.
        uuid:
          type: string
          description: Code du coupon tel que saisi par le client.
        plans:
          type: 'null'
          description: 'Colonne morte côté application : toujours nulle, quel que soit le coupon.'
        price:
          type:
          - string
          - 'null'
          description: Remise fixe, en chaîne décimale, si le coupon en applique une.
        percent_off:
          type:
          - string
          - 'null'
          description: Remise en pourcentage, en fraction chaîne ("0.10" pour 10 %).
        duration:
          type: string
          description: Portée du coupon (`once`, `repeating`, `forever` sur d'anciens coupons).
        redeem_by:
          type: integer
          description: Date limite d'utilisation, horodatage Unix.
        display_amount:
          type:
          - string
          - 'null'
          description: Libellé de la remise fixe, prêt à afficher.
        display_percent:
          type:
          - string
          - 'null'
          description: Libellé de la remise en pourcentage, prêt à afficher.
    OrderItem:
      type: object
      description: Ligne d'une commande.
      required:
      - id
      - price
      - tax
      - quantity
      - name
      - type
      - orderable
      - affilae
      - external_id
      - ref
      properties:
        id:
          type: integer
          description: Identifiant interne de la ligne.
        price:
          type: number
          description: Prix unitaire, nombre flottant.
        tax:
          type: string
          description: Taux de TVA de la ligne, en chaîne ("0.200" pour 20 %).
        quantity:
          type: integer
          description: Quantité commandée.
        name:
          type: string
          description: Nom du produit au moment de la commande.
        type:
          type: string
          description: 'Type de produit vendu : formule ou produit (valeur technique).'
        orderable:
          $ref: '#/components/schemas/Product'
          description: '`null` si le produit ou la formule a été supprimé depuis. Voir le schéma Product,
            déjà nullable.'
        affilae:
          type:
          - string
          - 'null'
          description: Identifiant de suivi d'affiliation, si applicable.
        external_id:
          type:
          - string
          - 'null'
          description: Identifiant d'origine du produit (import Exodus).
        ref:
          type:
          - string
          - 'null'
          description: Référence marchande du produit commandé.
    Order:
      type: object
      description: Commande passée par un client.
      required:
      - order_id
      - user_id
      - user_uuid
      - first_name
      - last_name
      - phone
      - email
      - status
      - created
      - items
      - shipping
      - count
      - total_tax_paid
      - total_discount_inc
      - total_paid
      - total_shipping_paid
      - created_at
      - coupon
      - subscription_uuid
      - revenue
      - refunded
      - customerRevenue
      - customerRefund
      - totalTransactionsSuccess
      - subTransactionsSuccess
      - next_billing
      - countTransactions
      - choices
      - retry_link
      - paid_transaction_id
      - paid_class_key
      - prestashop_order_id
      properties:
        order_id:
          type: integer
          description: Identifiant interne de la commande.
        user_id:
          type: integer
          description: Identifiant interne du client.
        user_uuid:
          type: string
          description: Identifiant public du client.
        first_name:
          type:
          - string
          - 'null'
          description: Prénom du client, depuis l'adresse de la commande.
        last_name:
          type:
          - string
          - 'null'
          description: Nom du client, depuis l'adresse de la commande.
        phone:
          type:
          - string
          - 'null'
          description: Téléphone au format E.164 si analysable.
        email:
          type: string
          format: email
          description: Adresse e-mail du client.
        status:
          type: string
          enum:
          - canceled
          - completed
          - failed
          - in_creation
          - in_process
          - pending
          - need_action
          - withdrawal_requested
          description: Statut de la commande.
        created:
          type: string
          format: date-time
          description: Date de création, toujours en UTC.
        items:
          type: array
          description: Lignes de la commande.
          items:
            $ref: '#/components/schemas/OrderItem'
        shipping:
          type: string
          description: Frais de port, en chaîne décimale à point.
        count:
          type: integer
          description: Nombre de lignes dans la commande.
        total_tax_paid:
          type: string
          description: Montant de TVA payé, en chaîne décimale à VIRGULE.
        total_discount_inc:
          type: string
          description: Montant de remise, en chaîne décimale à VIRGULE.
        total_paid:
          type:
          - string
          - 'null'
          description: Montant total payé, en chaîne décimale à VIRGULE. `null` tant que non calculé.
        total_shipping_paid:
          type: string
          description: Frais de port payés, en chaîne décimale à VIRGULE.
        created_at:
          type: string
          format: date-time
          description: Date de création de la commande, en UTC.
        coupon:
          oneOf:
          - $ref: '#/components/schemas/Coupon'
          - type: array
            maxItems: 0
          description: 'Premier coupon appliqué à la commande. Tableau vide `[]` s''il n''y en a pas :
            le type change selon le cas.'
        subscription_uuid:
          type:
          - string
          - 'null'
          description: Identifiant public de l'abonnement à l'origine de la commande, s'il y en a un.
        revenue:
          type:
          - string
          - 'null'
          description: Chiffre d'affaires de l'abonnement lié, en chaîne décimale à POINT. `null` sans
            abonnement.
        refunded:
          type:
          - string
          - 'null'
          description: Montant remboursé sur l'abonnement lié, en chaîne décimale à POINT.
        customerRevenue:
          type: string
          description: Chiffre d'affaires du client, en chaîne décimale à POINT.
        customerRefund:
          type: string
          description: Montant remboursé au client, en chaîne décimale à POINT.
        totalTransactionsSuccess:
          type: integer
          description: Nombre de transactions réussies du client, tous abonnements confondus.
        subTransactionsSuccess:
          type:
          - integer
          - 'null'
          description: Nombre de transactions réussies sur l'abonnement seul. `null` sans abonnement.
        next_billing:
          type:
          - string
          - 'null'
          format: date-time
          description: Prochaine date de facturation de l'abonnement lié.
        countTransactions:
          type: integer
          description: Nombre de transactions de la dernière commande de l'abonnement lié.
        choices:
          type: 'null'
          description: 'Toujours nul dans l''état actuel du code : aucun accesseur correspondant sur le
            modèle abonnement.'
        retry_link:
          type:
          - string
          - 'null'
          format: uri
          description: Lien de reprise de paiement. `null` sans secret client (`client_secret`) disponible.
        paid_transaction_id:
          type:
          - string
          - 'null'
          description: Identifiant de la transaction payée.
        paid_class_key:
          type:
          - string
          - 'null'
          description: Passerelle de paiement de la transaction payée.
        prestashop_order_id:
          type:
          - integer
          - 'null'
          description: Identifiant de la commande côté PrestaShop, pour les boutiques PrestaShop.
    Transaction:
      type: object
      description: Transaction de paiement liée à une commande.
      required:
      - order_id
      - order
      - gateway
      - transaction_id
      - detail
      - token
      - tenant_id
      - paid
      - amount
      - refunded
      - source_id
      - failure_message
      - failure_code
      - invoice_id
      - amount_refunded
      - statement_descriptor
      - user_id
      - created_at
      - updated_at
      - payment_intent_id
      properties:
        order_id:
          type: integer
          description: Identifiant interne de la commande.
        order:
          anyOf:
          - $ref: '#/components/schemas/Order'
          - type: 'null'
          description: Commande complète. `null` si la commande a été supprimée.
        gateway:
          type: string
          description: Passerelle de paiement utilisée.
        transaction_id:
          type:
          - string
          - 'null'
          description: Identifiant de la transaction côté passerelle.
        detail:
          description: Détail libre selon la passerelle, structure non garantie. Peut être absent ou nul.
        token:
          description: Jeton de paiement libre selon la passerelle. Peut être absent ou nul.
        tenant_id:
          type: integer
          description: Identifiant interne de la boutique.
        paid:
          type: boolean
          description: Vrai si la transaction est payée.
        amount:
          type: string
          description: Montant de la transaction, en chaîne décimale à deux décimales.
        refunded:
          type: boolean
          description: Vrai si la transaction a été remboursée.
        source_id:
          type:
          - string
          - 'null'
          description: Identifiant du moyen de paiement utilisé.
        failure_message:
          type:
          - string
          - 'null'
          description: Message d'échec, si la transaction a échoué.
        failure_code:
          type:
          - string
          - 'null'
          description: Code d'échec, si la transaction a échoué.
        invoice_id:
          type:
          - integer
          - 'null'
          description: Identifiant de la facture liée, si elle existe déjà.
        amount_refunded:
          type: string
          description: Montant remboursé, en chaîne décimale à deux décimales.
        statement_descriptor:
          type:
          - string
          - 'null'
          description: Libellé affiché sur le relevé bancaire du client.
        user_id:
          type:
          - integer
          - 'null'
          description: Identifiant interne du client.
        created_at:
          type: string
          format: date-time
          description: Date de création de la transaction.
        updated_at:
          type: string
          format: date-time
          description: Date de dernière modification de la transaction.
        payment_intent_id:
          type:
          - string
          - 'null'
          description: Identifiant de l'intention de paiement côté passerelle.
    Invoice:
      type: object
      description: Facture émise pour une commande payée.
      required:
      - order_id
      - customer
      - tenant_id
      - emitted_at
      - company
      - number
      - total_tax
      - total_discount
      - total
      - total_shipping
      - more
      - uuid
      - order
      - gateway
      properties:
        order_id:
          type: integer
          description: Identifiant interne de la commande facturée.
        customer:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: Instantané des coordonnées du client à la date d'émission de la facture, structure
            libre.
        tenant_id:
          type: integer
          description: Identifiant interne de la boutique.
        emitted_at:
          type: string
          format: date-time
          description: Date d'émission. Sort à minuit UTC (`T00:00:00.000000Z`), sans heure.
        company:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: Coordonnées de la boutique à la date d'émission de la facture, structure libre.
        number:
          type: integer
          description: Numéro de facture, séquentiel.
        total_tax:
          type: string
          description: Montant de TVA, en chaîne décimale à trois décimales.
        total_discount:
          type: string
          description: Montant de remise, en chaîne décimale à trois décimales.
        total:
          type: string
          description: Montant total, en chaîne décimale à trois décimales.
        total_shipping:
          type: string
          description: Frais de port, en chaîne décimale à trois décimales.
        more:
          type:
          - string
          - 'null'
          description: Mention complémentaire libre.
        uuid:
          type: string
          description: Identifiant public de la facture, composé du préfixe, de l'année, du mois et du
            numéro (exemple `F2026-09-42`).
        order:
          anyOf:
          - $ref: '#/components/schemas/Order'
          - type: 'null'
          description: Commande facturée.
        gateway:
          type:
          - string
          - 'null'
          description: Passerelle de la transaction payée. `null` sans transaction payée.
    Shipment:
      type: object
      description: Expédition planifiée ou réalisée.
      required:
      - tenant_id
      - id
      - uuid
      - address
      - transporter
      - created_at
      - updated_at
      - scheduled_at
      - tracking_link
      - subscription_uuid
      - address_id
      - phone
      - status
      - tracking_number
      - customer_id
      - supply_id
      - transporter_id
      - first_name
      - last_name
      - postcode
      - address1
      - address2
      - division
      - city
      - country
      - mail
      - subscriptions_count
      - transporter_name
      - shipped_count
      - site_name
      - options
      - iso_code
      - relay
      - relay_id
      - orderable_name
      - orderable_id
      - orderable_type
      - order_id
      - ref
      - customization_products
      - extra_customization_products
      properties:
        tenant_id:
          type: integer
          description: Identifiant interne de la boutique.
        id:
          type: integer
          description: Identifiant interne de l'expédition.
        uuid:
          type: string
          description: Identifiant public de l'expédition.
        address:
          anyOf:
          - $ref: '#/components/schemas/Address'
          - type: 'null'
          description: Adresse de livraison au moment de l'envoi.
        transporter:
          anyOf:
          - $ref: '#/components/schemas/Transporter'
          - type: 'null'
          description: Transporteur choisi pour cette expédition.
        created_at:
          type: string
          format: date-time
          description: Date de création de l'expédition.
        updated_at:
          type: string
          format: date-time
          description: Date de dernière modification de l'expédition.
        scheduled_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Date prévue d'expédition.
        tracking_link:
          type:
          - string
          - 'null'
          format: uri
          description: Lien de suivi. `null` sans numéro de suivi (combiné à l'URL de suivi du transporteur)
            ni URL de suivi enregistrée directement sur l'expédition.
        subscription_uuid:
          type:
          - string
          - 'null'
          description: Identifiant public de l'abonnement lié, s'il y en a un.
        address_id:
          type:
          - integer
          - 'null'
          description: Identifiant interne de l'adresse de livraison.
        phone:
          type:
          - string
          - 'null'
          description: Téléphone du destinataire.
        status:
          type: string
          description: Statut libre de l'expédition (`created`, `shipped`...).
        tracking_number:
          type:
          - string
          - 'null'
          description: Numéro de suivi transporteur.
        customer_id:
          type:
          - string
          - 'null'
          description: Identifiant public du client (`cus_...`), pas son identifiant numérique.
        supply_id:
          type:
          - string
          - 'null'
          description: Identifiant d'approvisionnement, si applicable.
        transporter_id:
          type:
          - integer
          - 'null'
          description: Identifiant interne du transporteur.
        first_name:
          type:
          - string
          - 'null'
          description: Prénom du destinataire, dénormalisé sur l'expédition.
        last_name:
          type:
          - string
          - 'null'
          description: Nom du destinataire, dénormalisé sur l'expédition.
        postcode:
          type:
          - string
          - 'null'
          description: Code postal de livraison, dénormalisé.
        address1:
          type:
          - string
          - 'null'
          description: Ligne d'adresse, dénormalisée.
        address2:
          type:
          - string
          - 'null'
          description: Complément d'adresse, dénormalisé.
        division:
          type:
          - string
          - 'null'
          description: Subdivision administrative, dénormalisée.
        city:
          type:
          - string
          - 'null'
          description: Ville de livraison, dénormalisée.
        country:
          type:
          - string
          - 'null'
          description: Nom du pays de livraison, dénormalisé.
        mail:
          type:
          - string
          - 'null'
          format: email
          description: Adresse e-mail du client, dénormalisée.
        subscriptions_count:
          type:
          - string
          - 'null'
          description: 'Nombre d''abonnements du client. Colonne texte : c''est une CHAÎNE, pas un nombre.'
        transporter_name:
          type:
          - string
          - 'null'
          description: Nom du transporteur, dénormalisé.
        shipped_count:
          type:
          - string
          - 'null'
          description: 'Nombre d''expéditions déjà livrées. Colonne texte : c''est une CHAÎNE, pas un
            nombre.'
        site_name:
          type:
          - string
          - 'null'
          description: Nom de la boutique.
        options:
          type:
          - string
          - 'null'
          description: Valeurs de déclinaison choisies, séparées par `|`.
        iso_code:
          type:
          - string
          - 'null'
          description: Code langue ou pays interne, en minuscules.
        relay:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: Point relais choisi, si la livraison passe par un point relais.
        relay_id:
          type:
          - string
          - 'null'
          description: Identifiant du point relais choisi.
        orderable_name:
          type:
          - string
          - 'null'
          description: Nom du produit ou de la formule expédiée, au moment de l'expédition.
        orderable_id:
          type:
          - string
          - 'null'
          description: 'Identifiant du produit ou de la formule expédiée. Colonne texte : c''est une CHAÎNE,
            pas un nombre.'
        orderable_type:
          type:
          - string
          - 'null'
          description: "Type de produit expédié : formule (App\\Plan) ou produit (App\\Product)."
        order_id:
          type:
          - integer
          - 'null'
          description: Identifiant interne de la commande liée, si elle existe.
        ref:
          type:
          - string
          - 'null'
          description: Référence marchande du produit ou de la formule expédiée.
        customization_products:
          type:
          - array
          - 'null'
          items: {}
          description: Choix de personnalisation au moment de l'expédition.
        extra_customization_products:
          type:
          - array
          - 'null'
          items: {}
          description: Personnalisations supplémentaires ajoutées après coup.
    Subscription:
      type: object
      description: Abonnement d'un client.
      required:
      - id
      - uuid
      - plan
      - address
      - transporter
      - created_at
      - end_date
      - engaged_date
      - auto_pause_at
      - start_date
      - active
      - paused
      - next_billing
      - update_transporter_at
      - relay
      - graceMonths
      - user_uuid
      - user_id
      - email
      - revenue
      - refunded
      - customerRevenue
      - customerRefund
      - totalTransactionsSuccess
      - subTransactionsSuccess
      - countTransactions
      - customization_products
      - retry_link
      - is_auto
      - interval
      - interval_count
      - switch_plan_uuid
      - switch_plan_date
      - referrer
      - display_interval
      - display_content
      - external_fingerprint
      - content
      properties:
        id:
          type: integer
          description: Identifiant interne de l'abonnement.
        uuid:
          type: string
          description: Identifiant public de l'abonnement.
        plan:
          anyOf:
          - $ref: '#/components/schemas/Plan'
          - type: 'null'
          description: Formule de l'abonnement. `null` si la formule liée a été supprimée.
        address:
          anyOf:
          - $ref: '#/components/schemas/Address'
          - type: 'null'
          description: Adresse de livraison de l'abonnement.
        transporter:
          anyOf:
          - $ref: '#/components/schemas/Transporter'
          - type: 'null'
          description: Transporteur choisi pour l'abonnement.
        created_at:
          type: string
          format: date-time
          description: Date de création de l'abonnement.
        end_date:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de fin de la période en cours.
        engaged_date:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de fin d'engagement, si la formule en impose un.
        auto_pause_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de mise en pause automatique programmée.
        start_date:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de début de l'abonnement.
        active:
          type: boolean
          description: Vrai si l'abonnement est actif.
        paused:
          type: boolean
          description: 'Reflète la colonne interne `grace_period` : vrai si l''abonnement est en pause.'
        next_billing:
          type:
          - string
          - 'null'
          format: date-time
          description: Prochaine date de facturation.
        update_transporter_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Date de la dernière mise à jour du transporteur.
        relay:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: Point relais choisi, si la livraison passe par un point relais.
        graceMonths:
          type:
          - integer
          - 'null'
          description: Nombre de mois de pause déjà accordés.
        user_uuid:
          type: string
          description: Identifiant public du client.
        user_id:
          type: integer
          description: Identifiant interne du client.
        email:
          type: string
          format: email
          description: Adresse e-mail du client au moment de l'envoi.
        revenue:
          type: string
          description: Chiffre d'affaires de l'abonnement, en chaîne décimale à point.
        refunded:
          type: string
          description: Montant remboursé sur l'abonnement, en chaîne décimale à point.
        customerRevenue:
          type: string
          description: Chiffre d'affaires du client, en chaîne décimale à point.
        customerRefund:
          type: string
          description: Montant remboursé au client, en chaîne décimale à point.
        totalTransactionsSuccess:
          type: integer
          description: Nombre de transactions réussies du client, tous abonnements confondus.
        subTransactionsSuccess:
          type: integer
          description: Nombre de transactions payées de l'abonnement.
        countTransactions:
          type: integer
          description: Nombre de transactions de la dernière commande de l'abonnement, pas le total.
        customization_products:
          type:
          - array
          - 'null'
          items: {}
          description: Choix de personnalisation de produits.
        retry_link:
          type: 'null'
          description: 'Toujours nul dans l''état actuel du code : aucun accesseur correspondant sur le
            modèle abonnement.'
        is_auto:
          type: boolean
          description: Vrai si la date de mise en pause automatique tombe à cinq jours de la date du jour.
        interval:
          type:
          - string
          - 'null'
          description: Unité de périodicité (`month`, `week`...).
        interval_count:
          type:
          - integer
          - 'null'
          description: Nombre d'unités entre deux échéances.
        switch_plan_uuid:
          type:
          - string
          - 'null'
          description: Identifiant public de la formule vers laquelle un changement est programmé.
        switch_plan_date:
          type:
          - string
          - 'null'
          format: date-time
          description: Date d'effet d'un changement de formule programmé.
        referrer:
          type:
          - string
          - 'null'
          description: Origine de l'abonnement, si connue.
        display_interval:
          type:
          - string
          - 'null'
          description: Libellé humain de la périodicité. `null` si `interval` est absent.
        display_content:
          type: string
          description: Noms des produits de l'abonnement, joints par une virgule.
        external_fingerprint:
          type:
          - string
          - 'null'
          description: Empreinte panier PrestaShop. `null` sans métadonnée d'origine.
        content:
          type: array
          description: Produits composant l'abonnement.
          items:
            type: object
            properties:
              external_id:
                type: string
                description: Identifiant d'origine du produit.
              quantity:
                type: integer
                description: Quantité de ce produit.
              product_id:
                type: integer
                description: Identifiant interne du produit.
              subscription_id:
                type: integer
                description: Identifiant interne de l'abonnement.
    User:
      type: object
      description: Compte client.
      required:
      - id
      - email
      - uuid
      - refp
      - has_active_subscription
      - info
      properties:
        id:
          type: integer
          description: Identifiant interne du client.
        email:
          type: string
          format: email
          description: Adresse e-mail du client.
        uuid:
          type: string
          description: Identifiant public du client.
        refp:
          type: string
          format: uri
          description: Lien de parrainage du client, construit sur le domaine de la boutique.
        has_active_subscription:
          type: boolean
          description: 'Calculé par une requête au moment de l''envoi : reflète l''état au moment de l''envoi,
            pas celui de l''événement.'
        info:
          type:
          - array
          - object
          - 'null'
          items: {}
          description: Commentaires libres associés au client. Objet JSON si le contenu enregistré a
            des clés, tableau sinon.
    Optin:
      type: object
      description: 'Inscription à une liste marketing, pour un couple adresse e-mail et boutique. Pas
        d''identifiant : la clé fonctionnelle est ce couple.'
      required:
      - email
      - tenant_id
      - valid
      - list_id
      properties:
        email:
          type: string
          format: email
          description: Adresse e-mail inscrite.
        tenant_id:
          type: integer
          description: Identifiant interne de la boutique.
        valid:
          type: boolean
          description: Vrai si l'inscription est active.
        list_id:
          type:
          - string
          - 'null'
          description: Identifiant de la liste marketing, côté prestataire d'e-mail.
    ChangedOnCreate:
      type: array
      maxItems: 0
      description: 'Toujours un tableau vide sur un événement de création : rien n''a encore changé.'
    ChangedOnUpdate:
      type: object
      additionalProperties: true
      description: 'Objet libre `{colonne: valeur brute}` : les champs réellement modifiés lors de l''écriture,
        avec leur valeur brute telle que stockée en base (pas la valeur mise en forme par l''API). Peut
        contenir des champs que vous n''avez pas modifiés vous-même : voir la page « Comprendre un envoi
        » pour les cas connus.'
    ChangedOnDelete:
      type: object
      properties:
        deleted:
          type: boolean
          const: true
          description: Toujours vrai.
      required:
      - deleted
      additionalProperties: false
      description: Marque la suppression. Le reste de la charge utile est la dernière valeur connue de
        l'objet.
    WebhookEventType:
      type: string
      description: 'Les 17 clés que l''API vendors accepte pour déclarer ou supprimer une URL de réception.
        `updated_change_choices` n''en fait pas partie : une tentative avec cette valeur renvoie une erreur
        422.'
      enum:
      - created_subscription
      - updated_subscription
      - deleted_subscription
      - created_checkoutorder
      - updated_checkoutorder
      - created_checkouttransaction
      - updated_checkouttransaction
      - created_checkoutinvoice
      - created_address
      - updated_address
      - created_shippingbox
      - updated_shippingbox
      - deleted_shippingbox
      - created_user
      - updated_user
      - created_optin
      - updated_optin
    ConfigurationResponse:
      type: object
      description: Réponse renvoyée après la déclaration ou la suppression d'une URL de réception.
      required:
      - data
      properties:
        data:
          type: object
          properties:
            id:
              type: integer
              description: Identifiant interne de la boutique.
            host:
              type: string
              description: Domaine de la boutique.
            name:
              type: string
              description: Nom de la boutique.
            paymentMethods:
              type: array
              description: Moyens de paiement actifs sur la boutique.
              items: {}
            webhooks:
              type: object
              description: 'Objet complet des URL de réception déclarées après cette modification, sous
                la forme `{clé d''événement: URL}`.'
              additionalProperties:
                type: string
                format: uri
            metadata:
              type:
              - object
              - 'null'
              additionalProperties: true
              description: Métadonnées internes de la boutique, non contractuelles. Peut être `null`.
