Repository navigation
Expand file tree
/
Copy pathopenapi.yaml
More file actions
4731 lines (4718 loc) · 162 KB
/
Copy pathopenapi.yaml
File metadata and controls
4731 lines (4718 loc) · 162 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
openapi: 3.1.0
servers:
- url: https://{environment}.ciklik.co/api/v3
description: Serveur API
variables:
environment:
default: app
enum:
- app # URL de Production
- demonstration # URL de Test/Démonstration
info:
title: Ciklik API
version: 1.0.0
description: "# Bienvenue sur l'API Ciklik. Vous pouvez l'utiliser pour accéder aux ressources de votre compte, ou lancer des actions de base.\n## API Endpoints\nThe base endpoint is https://{environment}.ciklik.co/api/v3\n## Authentification\nL'API s'authentifie par un jeton d'API Ciklik (jeton opaque, ce n'est pas un JWT) envoyé dans l'en-tête `Authorization: Bearer`.\n\nExemple :\n ```curl\n curl --location 'https://{environment}.ciklik.co/api/v3/whoiam' --header 'Authorization: Bearer <Ciklik Token>'\n ```\n\nUne requête sans jeton ou avec un jeton invalide reçoit `401` ; un jeton valide qui n'a pas accès à la ressource reçoit `403`.\n## Request Limit\nLe nombre de requêtes est limité à 200 par minute, compté par adresse IP (toutes les requêtes émises depuis la même adresse IP partagent ce quota). Si la limite est atteinte, la réponse http sera `429 Too Many Requests`.\n## Filtres\nCertains endpoints de l'API prennent en charge le filtrage des résultats sur un ensemble spécifié de champs. Chaque endpoint qui prend en charge le filtrage énumérera les champs qui peuvent être filtrés. Plusieurs champs peuvent être combinés en utilisant un caractère esperluette &\n\nExemple: `/subscriptions?filter[activated]=1&filter[created_at_after]=2026-01-01&sort=-created_at`\n\nUn filtre ou un tri qui n'est pas dans la liste de l'endpoint est refusé avec une réponse `400 Bad Request` (les endpoints qui valident eux-mêmes le paramètre `sort` répondent `422` pour un tri hors liste). Une valeur de filtre au mauvais format peut être refusée en `422`.\n## Pagination\nLes listes de résultats de l'API sont paginées. La pagination est fixe : 20 éléments par page, quelle que soit la valeur d'un éventuel paramètre `per_page` (ignoré). Vous pouvez obtenir les différentes pages avec la query string `page`, default value `1`.\n\nLe nombre total d'éléments dans une liste est disponible dans le champ `meta.total`.\n\nExemple: `?page=2`\n"
tags:
- name: tenant
description: Opérations liées à l'entité "Website".
- name: products
description: Opérations liées aux produits
- name: webhooks
description: Opérations liées aux webhooks
- name: subscriptions
description: Opérations liées aux abonnements
- name: deliveries
description: Opérations liées aux bons de livraison
- name: kpis
description: Opérations liées aux indicateurs de performance
- name: addresses
description: Opérations liées aux adresses
- name: customers
description: Opérations liées aux comptes utilisateurs
- name: transactions
description: Opérations liées aux transactions
- name: rebill-errors
description: Opérations liées aux erreurs de rebill
- name: orders
description: Opérations liées aux commandes
paths:
/whoiam:
get:
summary: Récupérer les données de l'entité "Website"
description: Renvoie le site du jeton, enveloppé dans `data`. Le champ `metadata` contient toutes les clés de métadonnées du site, y compris celles qui n'ont pas été posées par l'API.
tags:
- tenant
security:
- bearerAuth: []
responses:
200:
description: Réponse réussie
content:
application/json:
schema:
$ref: "#/components/schemas/WebsiteEntity"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
put:
summary: Mettre à jour les métadonnées d'un site web (WebsiteEntity)
description: Permet de mettre à jour les métadonnées d'un site web (WebsiteEntity) en modifiant ou ajoutant des clés/valeurs au champ "metadata". Les clés envoyées sont fusionnées avec les métadonnées existantes ; les clés absentes du corps sont conservées (elles ne sont pas effacées). La réponse renvoie le site complet, enveloppé dans `data`.
tags:
- tenant
security:
- bearerAuth: [ ]
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
metadata:
type: object
additionalProperties:
type: [string, "null"]
maxLength: 5000
description: |
Métadonnées personnalisées. Les valeurs doivent être des chaînes de caractères (max 5000 caractères) ou null.
Seules les clés envoyées sont modifiées, les autres clés du site sont conservées.
Les clés commençant par "ciklik_encrypted_" seront automatiquement encryptées.
example:
metadata:
key1: "value1"
key2: "value2"
ciklik_encrypted_key2: "value-encrypted"
responses:
200:
description: Succès - Les métadonnées ont été mises à jour avec succès.
content:
application/json:
schema:
$ref: "#/components/schemas/WebsiteEntity"
422:
description: Erreur de validation
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
/installation:
get:
operationId: getInstallation
summary: Récupérer l'état d'installation de l'espace
description: |
Route destinée au back-office du module PrestaShop : elle renvoie l'état d'installation de l'espace du jeton (espace de test ou de production, étapes franchies, ce qui reste à faire et adresse de la page de suivi d'installation).
Les libellés sont traduits dans la langue de la requête. L'état est lu tel qu'il a été relevé lors des dernières vérifications : aucune vérification n'est lancée pendant la requête.
tags:
- tenant
security:
- bearerAuth: [ ]
responses:
200:
description: Réponse réussie
content:
application/json:
schema:
type: object
properties:
data:
$ref: "#/components/schemas/InstallationState"
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
429:
$ref: '#/components/responses/TooManyRequests'
/products:
get:
summary: Récupérer une liste de produits
tags:
- products
security:
- bearerAuth: [ ]
parameters:
- name: filter[id]
in: query
description: Filtrer par ID du produit
schema:
type: integer
example: 1
- name: filter[name]
in: query
description: Filtrer par nom du produit
schema:
type: string
example: "Produit exemple"
- name: filter[id_category]
in: query
description: Filtrer par ID de catégorie
schema:
type: integer
example: 5
- name: filter[quantity]
in: query
description: Filtrer par quantité
schema:
type: integer
example: 10
- name: filter[price]
in: query
description: Filtrer par prix
schema:
type: number
example: 29.99
- name: filter[active]
in: query
description: Filtrer par état d'activité
schema:
type: boolean
example: true
- name: filter[slug]
in: query
description: Filtrer par slug
schema:
type: string
example: "produit-exemple"
- name: filter[virtual_product]
in: query
description: Filtrer par produit virtuel (correspondance exacte)
schema:
type: boolean
- name: filter[salable]
in: query
description: Filtrer par produit vendable
schema:
type: boolean
- name: filter[gift_card]
in: query
description: Filtrer par carte cadeau
schema:
type: boolean
- name: filter[gift_plan]
in: query
description: Filtrer par plan cadeau (UUID du plan, correspondance partielle)
schema:
type: string
- name: filter[disable_seo]
in: query
description: Filtrer par désactivation SEO (correspondance exacte)
schema:
type: boolean
- name: filter[ref]
in: query
description: Filtrer par référence
schema:
type: string
- name: filter[composable]
in: query
description: Filtrer par composable
schema:
type: boolean
- name: sort
in: query
description: "Trier les résultats (ex: -name pour un tri descendant). Tri par défaut : `-id` (plus récent en premier)."
schema:
type: string
enum:
- id
- name
- id_category
- quantity
- price
- active
- slug
- virtual_product
- salable
- gift_card
- gift_plan
- disable_seo
- ref
- composable
- created_at
- updated_at
- -id
- -name
- -id_category
- -quantity
- -price
- -active
- -slug
- -virtual_product
- -salable
- -gift_card
- -gift_plan
- -disable_seo
- -ref
- -composable
- -created_at
- -updated_at
- name: page
in: query
description: Numéro de la page pour la pagination
schema:
type: integer
- name: per_page
in: query
description: Ignoré. La pagination est fixe à 20 éléments par page.
schema:
type: integer
responses:
200:
description: Liste de produits récupérée avec succès
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: "#/components/schemas/ProductEntity"
links:
type: object
properties:
first:
type: string
last:
type: string
prev:
type: string
next:
type: string
meta:
$ref: "#/components/schemas/PaginationMeta"
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
429:
$ref: '#/components/responses/TooManyRequests'
post:
summary: Créer ou mettre à jour un produit
description: |
Crée le produit, ou le met à jour s'il existe déjà. Le produit est cherché par `external_id` s'il est fourni, sinon par `ref`.
Deux formes de corps sont acceptées :
- un produit seul (champs à la racine) : la réponse contient le produit dans `data`, avec le statut `201` s'il vient d'être créé et `200` s'il a été mis à jour ;
- un lot `{"products": [...]}` : chaque élément suit le schéma d'un produit seul, la réponse contient la liste des produits dans `data` avec le statut `200`.
À chaque enregistrement par cette route, le `slug` du produit est remplacé par un identifiant aléatoire (la valeur envoyée est ignorée) et `meta_title` reprend `name`.
Si `description` est absente, un produit existant garde la sienne et un nouveau produit en reçoit une vide.
tags:
- products
security:
- bearerAuth: [ ]
requestBody:
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ProductEntityCreation"
- type: object
description: Création ou mise à jour en lot
required:
- products
properties:
products:
type: array
items:
$ref: "#/components/schemas/ProductEntityCreation"
responses:
200:
description: Produit mis à jour (corps unitaire), ou lot traité (corps `products`, `data` est alors une liste)
content:
application/json:
schema:
type: object
properties:
data:
oneOf:
- $ref: "#/components/schemas/ProductEntity"
- type: array
items:
$ref: "#/components/schemas/ProductEntity"
201:
description: Produit créé (corps unitaire uniquement)
content:
application/json:
schema:
type: object
properties:
data:
$ref: "#/components/schemas/ProductEntity"
422:
description: Erreur de validation
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
/products/{ref}:
get:
summary: Récupérer un produit par référence (SKU)
tags:
- products
security:
- bearerAuth: [ ]
parameters:
- name: ref
in: path
description: Référence du produit (SKU)
required: true
schema:
type: string
responses:
200:
description: Produit récupéré avec succès
content:
application/json:
schema:
type: object
properties:
data:
$ref: "#/components/schemas/ProductEntity"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
404:
description: Produit non trouvé
429:
$ref: '#/components/responses/TooManyRequests'
patch:
summary: Mettre à jour un produit par référence (SKU)
description: |
Met à jour le produit dont la référence est dans le chemin. Si `external_id` est fourni dans le corps, le produit est cherché par `external_id` et la référence du chemin est ignorée.
`name`, `ref` et `price` sont obligatoires, même pour une mise à jour partielle.
tags:
- products
security:
- bearerAuth: [ ]
parameters:
- name: ref
in: path
description: Référence du produit (SKU)
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ProductEntityUpdate"
responses:
200:
description: Produit mis à jour avec succès
content:
application/json:
schema:
type: object
properties:
data:
$ref: "#/components/schemas/ProductEntity"
404:
description: Produit non trouvé (par référence du chemin, ou par `external_id` s'il est fourni)
422:
description: Erreur de validation
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
/webhooks:
get:
summary: Lister les derniers webhooks envoyés
description: Renvoie les trois derniers webhooks envoyés pour cet évènement, du plus récent au plus ancien. Chaque élément est la charge utile (payload) envoyée. Un `event_type` absent ou inconnu répond 404.
tags:
- webhooks
security:
- bearerAuth: [ ]
parameters:
- name: event_type
in: query
description: Nom de l'évènement à avoir
required: true
schema:
type: string
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
- deleted_user
- created_optin
- updated_optin
responses:
200:
description: Les trois derniers webhooks envoyés pour cet évènement (liste vide si aucun)
content:
application/json:
schema:
type: object
properties:
data:
type: array
description: Charges utiles envoyées, de la plus récente à la plus ancienne
items:
type: object
additionalProperties: true
404:
description: Paramètre `event_type` absent ou nom d'évènement inconnu
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
post:
summary: Ajouter un webhook
description: |
Enregistre l'adresse appelée pour un type d'évènement. Il n'y a qu'une adresse par type d'évènement : un nouvel enregistrement remplace l'adresse précédente pour ce type, y compris celle posée par le module PrestaShop.
La réponse renvoie le site, enveloppé dans `data`, avec la liste des webhooks actifs.
tags:
- webhooks
security:
- bearerAuth: [ ]
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
event_type:
type: string
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
- deleted_user
- created_optin
- updated_optin
hook_url:
type: string
format: uri
description: URL http ou https publique, joignable depuis internet. Les adresses internes, privées ou réservées (localhost, 10.x, 192.168.x, 169.254.x...) sont refusées en 422.
required:
- hook_url
- event_type
responses:
200:
description: Webhook ajouté avec succès (site enveloppé dans `data`)
content:
application/json:
schema:
$ref: "#/components/schemas/WebsiteEntity"
422:
description: Erreur de validation (`event_type` inconnu, `hook_url` absente, mal formée ou non publique)
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
delete:
summary: Supprimer un webhook
description: Retire l'adresse enregistrée pour ce type d'évènement. La réponse renvoie le site, enveloppé dans `data`.
tags:
- webhooks
security:
- bearerAuth: [ ]
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
event_type:
type: string
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
- deleted_user
- created_optin
- updated_optin
required:
- event_type
responses:
200:
description: Webhook supprimé avec succès
content:
application/json:
schema:
$ref: "#/components/schemas/WebsiteEntity"
422:
description: Erreur de validation
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
429:
$ref: '#/components/responses/TooManyRequests'
/subscriptions/{id}:
get:
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Obtenir les détails d'un abonnement
parameters:
- name: id
in: path
description: UUID de l'abonnement
required: true
schema:
type: string
responses:
200:
description: Réponse réussie
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/SubscriptionEntity'
403:
description: Accès interdit
401:
description: Authentification requise (jeton absent ou invalide)
404:
description: Abonnement introuvable
429:
$ref: '#/components/responses/TooManyRequests'
put:
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Mettre à jour un abonnement
description: |
Permet de mettre à jour un abonnement, le résilier, le mettre en pause pour 1, 2 ou 3 mois, ou le relancer.
L'adresse (`address_id`), le transporteur (`transporter`) et la déclinaison (`declinaison_id`) sont contrôlés avant toute écriture : une référence inconnue ou qui n'appartient pas au site répond 404 et l'abonnement n'est pas modifié.
Les autres refus (422 sur `content` ou `next_billing`, par exemple) peuvent intervenir après l'application des changements qui les précèdent : n'envoyez qu'un changement par requête si vous avez besoin d'un résultat tout ou rien.
La réponse renvoie l'abonnement relu après les modifications, enveloppé dans `data`.
parameters:
- name: id
in: path
description: UUID de l'abonnement à mettre à jour
required: true
schema:
type: string
requestBody:
description: Données à modifier sur l'abonnement
required: true
content:
application/json:
schema:
type: object
properties:
address_id:
type: integer
description: ID d'une adresse du site à affecter à l'abonnement (404 si elle n'appartient pas au site).
declinaison_id:
type: integer
description: ID d'une déclinaison du site à affecter à l'abonnement (404 si elle n'appartient pas au site).
transporter:
type: integer
description: ID d'un transporteur du site (404 s'il n'appartient pas au site). Peut être accompagné d'un objet `relay` décrivant le point relais choisi.
relay:
type: object
additionalProperties: true
description: Point relais, lu uniquement avec `transporter`.
voucher:
type: boolean
description: Si vrai, applique l'offre de rétention (bon de réduction) proposée au désabonnement.
break:
type: boolean
description: Accepté par la validation, sans effet (la mise en pause passe par `pause` et `months`).
months:
type: number
format: float
minimum: 1
maximum: 3
description: Nombre de mois pour mettre en pause l'abonnement (1, 2 ou 3 mois) (!= résiliation). Obligatoire si `pause` vaut true.
active:
type: boolean
description: Indique si l'abonnement est actif.
notification:
type: boolean
description: Lors de la résiliation d'un abonnement, notifier ou non par email.
default: true
pause:
type: boolean
description: Indique si l'abonnement doit être mis en pause.
next_billing:
type: string
format: date
description: Modifier la date du prochain paiement (date postérieure à aujourd'hui). Refusé en 422 si une commande du cycle est en attente avec une relance programmée.
content:
type: array
description: nouveau contenu de l'abonnement, via external_id
items:
type: object
required:
- external_id
- quantity
- interval
- interval_count
properties:
external_id:
type: [string, integer]
maxLength: 255
description: Identifiant externe du produit, chaîne ou entier (255 caractères max). Les identifiants PrestaShop au format `produit:déclinaison` sont acceptés.
quantity:
type: integer
minimum: 1
description: Quantité du produit
interval:
type: string
description: Intervalle de renouvellement (`month`, `week` ou `day` attendus ; la valeur n'est pas contrôlée)
interval_count:
type: integer
minimum: 1
description: Nombre d'intervalles
example:
external_id: "123"
quantity: 1
interval: month
interval_count: 1
interval:
type: string
description: Nouvel intervalle de renouvellement (`month`, `week` ou `day`), lu seulement avec `interval_count`.
interval_count:
type: integer
description: Nouveau nombre d'intervalles, lu seulement avec `interval`.
upsells:
type: array
description: Produits complémentaires (PrestaShop) enregistrés dans l'empreinte du panier de l'abonnement.
items:
type: object
properties:
product_id:
type: integer
product_attribute_id:
type: integer
quantity:
type: integer
metadata:
type: object
additionalProperties:
type: string
maxLength: 5000
description: |
Métadonnées personnalisées pour l'abonnement. Les valeurs doivent être des chaînes de caractères (max 5000 caractères).
Les clés commençant par "ciklik_encrypted_" seront automatiquement encryptées.
example:
active: true
metadata:
key1: "value1"
key2: "value2"
ciklik_encrypted_key2: "value-encrypted"
responses:
200:
description: Succès - Abonnement mis à jour.
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/SubscriptionEntity'
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
404:
description: Abonnement introuvable, ou adresse, transporteur ou déclinaison qui n'appartient pas au site.
422:
description: Erreur de validation
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
429:
$ref: '#/components/responses/TooManyRequests'
delete:
operationId: deleteSubscription
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Supprimer un abonnement
description: >-
Supprime un abonnement (soft delete). L'abonnement doit être inactif
(active = false) et appartenir au tenant authentifié pour pouvoir être supprimé.
parameters:
- name: id
in: path
description: UUID de l'abonnement à supprimer.
required: true
schema:
type: string
responses:
200:
description: Succès - Abonnement supprimé.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
success:
type: boolean
example: true
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
404:
description: Non trouvé - Abonnement introuvable ou n'appartenant pas au tenant authentifié.
422:
description: Erreur de validation - L'abonnement est encore actif.
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"
429:
$ref: '#/components/responses/TooManyRequests'
/subscriptions/{id}/products/{external_id}:
patch:
operationId: updateSubscriptionProduct
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Modifier la quantité d'un produit dans un abonnement
description: Met à jour la quantité d'un produit spécifique déjà rattaché à l'abonnement. Les produits avec personnalisation (customization hash) ne sont pas modifiables.
parameters:
- name: id
in: path
description: UUID de l'abonnement
required: true
schema:
type: string
- name: external_id
in: path
description: Identifiant externe du produit (exodus_id)
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- quantity
properties:
quantity:
type: integer
minimum: 1
maximum: 9999
description: Nouvelle quantité du produit
example:
quantity: 3
responses:
200:
description: Quantité mise à jour avec succès
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/SubscriptionEntity'
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
404:
description: Abonnement ou produit introuvable
422:
description: Erreur de validation (quantité invalide, produit personnalisé, abonnement inactif). Un abonnement inactif ou un produit personnalisé répond avec un corps `{message}` sans `errors`.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ValidationError"
- $ref: "#/components/schemas/ErrorMessage"
429:
$ref: '#/components/responses/TooManyRequests'
delete:
operationId: removeSubscriptionProduct
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Retirer un produit d'un abonnement
description: Retire un produit spécifique de l'abonnement. Impossible de retirer le dernier produit. Les produits avec personnalisation ne sont pas modifiables.
parameters:
- name: id
in: path
description: UUID de l'abonnement
required: true
schema:
type: string
- name: external_id
in: path
description: Identifiant externe du produit (exodus_id)
required: true
schema:
type: string
responses:
200:
description: Produit retiré avec succès
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/SubscriptionEntity'
401:
description: Authentification requise (jeton absent ou invalide)
403:
description: Accès interdit
404:
description: Abonnement ou produit introuvable
422:
description: Erreur de validation (dernier produit, produit personnalisé, abonnement inactif). Un abonnement inactif ou un produit personnalisé répond avec un corps `{message}` sans `errors`.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ValidationError"
- $ref: "#/components/schemas/ErrorMessage"
429:
$ref: '#/components/responses/TooManyRequests'
/subscriptions/{id}/products:
post:
operationId: addSubscriptionProduct
tags:
- subscriptions
security:
- bearerAuth: [ ]
summary: Ajouter un produit à un abonnement
description: |
Ajoute un produit à l'abonnement ou met à jour la quantité si le produit est déjà rattaché (upsert).
Si le produit existe déjà en base (via son `external_id`), il est simplement rattaché ; les champs `price` et `tax` du corps sont alors ignorés.
Si le produit n'existe pas, il est créé automatiquement : `price` devient **obligatoire**, `tax` défaut à 0 si absent.
Les produits avec personnalisation (customization hash) ne sont pas acceptés.
Retourne 201 pour un nouvel attachement (nouveau produit créé ou produit existant nouvellement rattaché), 200 pour une mise à jour de quantité sur un produit déjà rattaché.
parameters:
- name: id
in: path
description: UUID de l'abonnement
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- external_id
- name
properties:
external_id:
type: string
maxLength: 255
description: Identifiant externe du produit (exodus_id)
name:
type: string
minLength: 1
maxLength: 255
description: Nom du produit (utilisé lors de la création automatique, ignoré si le produit existe déjà)
quantity: