openapi: 3.1.0
info:
  title: API côté serveur Adapty
  version: 1.0.0
servers:
  - url: https://api.adapty.io
    description: Serveur de production
paths:
  /api/v2/server-side-api/profile/:
    post:
      summary: Créer un profil
      description: Crée un nouvel utilisateur final de votre application dans Adapty.
      operationId: createProfile
      tags:
        - Profile
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-platform
          in: header
          required: false
          schema:
            type: string
          description: La plateforme de l'appareil sur lequel l'utilisateur a installé votre application
      responses:
        '200':
          description: Profil créé avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
              example:
                data:
                  app_id: "14c3d623-2f3a-455a-aa86-ef83dff6913b"
                  profile_id: "3286abd3-48b0-4e9c-a5f6-ac0a006804a6"
                  customer_user_id: "jane.doe@example.com"
                  total_revenue_usd: 0.0
                  segment_hash: "8f45947bad31ab0c"
                  timestamp: 1736425645861
                  custom_attributes:
                    - key: "favourite_sport"
                      value: "yoga"
                  access_levels: []
                  subscriptions: []
                  non_subscriptions: []
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: "non_field_errors"
                    errors: ["Authentication credentials were not provided."]
                error_code: "not_authenticated"
                status_code: 401
        '500':
          description: Erreur interne du serveur
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileRequest'
            example:
              first_name: "Jane"
              last_name: "Doe"
              gender: "f"
              email: "jane.doe@example.com"
              phone_number: "+1234567890"
              birthday: "2000-12-31"
              ip_country: "FR"
              store_country: "US"
              store: "app_store"
              analytics_disabled: true
              custom_attributes:
                - key: "favourite_sport"
                  value: "yoga"
              installation_meta:
                device_id: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
                device: "string"
                locale: "en"
                os: "string"
                platform: "iOS"
                timezone: "Europe/Rome"
                user_agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1"
                idfa: "EA7583CD-A667-48BC-B806-42ECB2B48333"
                idfv: "E9D48DA5-3930-4B41-8521-D953AECD2F33"
                advertising_id: ""
                android_id: ""
                android_app_set_id: ""
    get:
      summary: Obtenir un profil
      description: Récupère les détails d'un utilisateur final existant de votre application.
      operationId: getProfile
      tags:
        - Profile
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Profil récupéré avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
              example:
                data:
                  app_id: "14c3d333-2f3a-455a-aa86-ef83dff6913b"
                  profile_id: "d8533a10-bcce-4e33-8c9d-88b05ac56559"
                  customer_user_id: "77B14FB4-FD2A-4D38-AA3A-4C433F79863C"
                  total_revenue_usd: 9.99
                  segment_hash: "fdaeef7f8aaa33c9"
                  timestamp: 1733324566777
                  custom_attributes:
                    - key: "favourite_sport"
                      value: "yoga"
                  access_levels:
                    - access_level_id: "premium"
                      store: "app_store"
                      store_product_id: "unlimited.9999"
                      store_base_plan_id: null
                      store_transaction_id: "2000000335013007"
                      store_original_transaction_id: "2000000335013007"
                      offer: null
                      starts_at: null
                      purchased_at: "2024-12-24T10:50:23+00:00"
                      originally_purchased_at: "2024-12-24T10:50:23+00:00"
                      expires_at: null
                      renewal_cancelled_at: "2025-01-05T13:27:47.461425+00:00"
                      billing_issue_detected_at: null
                      is_in_grace_period: false
                      cancellation_reason: null
                  subscriptions:
                    - store: "app_store"
                      store_product_id: "unlimited.9999"
                      store_base_plan_id: null
                      store_transaction_id: "2000000815013007"
                      store_original_transaction_id: "2000000815013007"
                      offer: null
                      environment: "Sandbox"
                      purchased_at: "2024-12-24T10:50:23+00:00"
                      originally_purchased_at: "2024-12-24T10:50:23+00:00"
                      expires_at: null
                      renewal_cancelled_at: null
                      billing_issue_detected_at: null
                      is_in_grace_period: false
                      cancellation_reason: null
                  non_subscriptions:
                    - purchase_id: "7a5f9a7d-e236-33e6-96d8-53a3c59c5562"
                      store: "app_store"
                      store_product_id: "1year.premium"
                      store_base_plan_id: null
                      store_transaction_id: "30002109551456"
                      store_original_transaction_id: "30002109551456"
                      purchased_at: "2022-10-12T09:42:50+00:00"
                      environment: "Production"
                      is_refund: false
                      is_consumable: false
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: "non_field_errors"
                    errors: ["Authentication credentials were not provided."]
                error_code: "not_authenticated"
                status_code: 401
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: null
                    errors: ["Profile not found"]
                error_code: "profile_does_not_exist"
                status_code: 404
        '500':
          description: Erreur interne du serveur
    patch:
      summary: Mettre à jour un profil
      description: Mettre à jour les informations du profil utilisateur
      operationId: updateProfile
      tags:
        - Profile
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Profil mis à jour avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileRequest'
    delete:
      summary: Supprimer un profil
      description: Supprimer le profil utilisateur
      operationId: deleteProfile
      tags:
        - Profile
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '204':
          description: Profil supprimé avec succès
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur

  /api/v2/server-side-api/purchase/set/transaction/:
    post:
      summary: Définir une transaction
      description: |
        Crée une nouvelle transaction pour un utilisateur final de votre application dans Adapty et fournit un niveau d'accès. La transaction créée par cette méthode apparaîtra dans vos analyses et dans le fil d'événements, et sera envoyée à toutes les intégrations.

        **Mise à jour d'un abonnement existant :** Pour mettre à jour `billing_issue_detected_at` ou `renew_status_changed_at` sans générer de nouvel événement `subscription_renewed`, réutilisez le `store_transaction_id` existant (sous le même `store_original_transaction_id`). La soumission d'un nouveau `store_transaction_id` est traitée comme une nouvelle transaction et déclenche un événement `subscription_renewed`.
      operationId: setTransaction
      tags:
        - Purchase
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Transaction enregistrée avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransactionDTORequest'
            examples:
              one_time_purchase:
                summary: Exemple d'achat unique
                value:
                  purchase_type: "one_time_purchase"
                  store: "app_store"
                  environment: "Production"
                  store_product_id: "premium_lifetime"
                  store_transaction_id: "1000000123456789"
                  store_original_transaction_id: "1000000123456789"
                  is_family_shared: false
                  price:
                    country: "US"
                    currency: "USD"
                    value: 9.99
                  purchased_at: "2024-01-15T10:30:00Z"
                  variation_id: "variation_123"
                  offer: null
                  refunded_at: null
                  cancellation_reason: null
              subscription:
                summary: Exemple d'abonnement
                value:
                  purchase_type: "subscription"
                  store: "app_store"
                  environment: "Production"
                  store_product_id: "premium_monthly"
                  store_transaction_id: "1000000123456789"
                  store_original_transaction_id: "1000000123456789"
                  is_family_shared: false
                  price:
                    country: "US"
                    currency: "USD"
                    value: 4.99
                  purchased_at: "2024-01-15T10:30:00Z"
                  originally_purchased_at: "2024-01-15T10:30:00Z"
                  expires_at: "2024-02-15T10:30:00Z"
                  renew_status: true
                  renew_status_changed_at: null
                  billing_issue_detected_at: null
                  grace_period_expires_at: null
                  variation_id: "variation_123"
                  offer:
                    category: "introductory"
                    type: "free_trial"
                    id: "trial_offer_123"
                  refunded_at: null
                  cancellation_reason: null

  /api/v2/server-side-api/purchase/profile/grant/access-level/:
    post:
      summary: Accorder un niveau d'accès
      description: Fournit un niveau d'accès à votre utilisateur final sans fournir d'informations sur la transaction. Cela est utile si vous avez des bonus pour les parrainages ou d'autres événements liés à vos produits. Le niveau d'accès fourni par cette méthode ne sera pas reflété dans vos analyses. Il sera envoyé uniquement à l'intégration webhook, et n'apparaîtra dans le fil d'événements que dans ce cas.
      operationId: grantAccessLevel
      tags:
        - Purchase
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Niveau d'accès accordé avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GrantAccessRequest'
            examples:
              immediate_grant:
                summary: Accorder l'accès immédiatement
                value:
                  access_level_id: "premium"
              scheduled_grant:
                summary: Accorder l'accès avec des dates de début et de fin
                value:
                  access_level_id: "premium"
                  starts_at: "2024-01-01T00:00:00Z"
                  expires_at: "2024-12-31T23:59:59Z"
              lifetime_grant:
                summary: Accorder un accès à vie
                value:
                  access_level_id: "premium"
                  starts_at: "2024-01-01T00:00:00Z"

  /api/v2/server-side-api/purchase/profile/revoke/access-level/:
    post:
      summary: Révoquer un niveau d'accès
      description: Supprime un niveau d'accès d'un utilisateur final de votre application dans Adapty.
      operationId: revokeAccessLevel
      tags:
        - Purchase
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Niveau d'accès révoqué avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RevokeAccessRequest'
            examples:
              immediate_revoke:
                summary: Révoquer l'accès immédiatement
                value:
                  access_level_id: "premium"
              scheduled_revoke:
                summary: Planifier la révocation de l'accès
                value:
                  access_level_id: "premium"
                  revoke_at: "2024-12-31T23:59:59Z"

  /api/v2/server-side-api/purchase/profile/refund-saver/settings/:
    get:
      summary: Obtenir les paramètres de Refund Saver
      description: Obtenir la préférence Refund Saver pour cet utilisateur — si Adapty doit demander de refuser ou d'approuver sa demande de remboursement. Obtenir la préférence de partage de données pour cet utilisateur — si l'utilisateur a donné son consentement au partage de ses données avec Apple.
      operationId: getRefundSaverSettings
      tags:
        - Refund Saver
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Paramètres récupérés avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RefundSaverSettingsResponse'
              example:
                profile_id: "e5aab402-b1bd-4039-b632-57a91ebc0779"
                settings:
                  consent: true
                  custom_preference: "no_preference"
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                profile_does_not_exist:
                  summary: Le profil n'existe pas
                  value:
                    errors: ["Profile does not exist"]
                    error_code: "profile_does_not_exist"
                    status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      summary: Définir les paramètres de Refund Saver
      description: Définir la préférence de remboursement individuellement pour l'utilisateur et enregistrer si l'utilisateur a donné son consentement au partage de ses données. Par défaut, Refund Saver demande toujours à Apple de refuser la demande de remboursement d'un utilisateur. Vous pouvez modifier ce comportement par défaut pour tous les utilisateurs dans l'Adapty Dashboard, ou l'ajuster pour un utilisateur spécifique via le Dashboard, le SDK Adapty ou l'API côté serveur.
      operationId: setRefundSaverSettings
      tags:
        - Refund Saver
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundSaverSettingsRequest'
            example:
              custom_preference: "grant"
              consent: true
      responses:
        '200':
          description: Paramètres mis à jour avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RefundSaverSettingsResponse'
              example:
                profile_id: "e5aab402-b1bd-4039-b632-57a91ebc0779"
                settings:
                  consent: true
                  custom_preference: "grant"
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                profile_does_not_exist:
                  summary: Le profil n'existe pas
                  value:
                    errors: ["Profile does not exist"]
                    error_code: "profile_does_not_exist"
                    status_code: 400
                validation_error:
                  summary: Erreur de validation
                  value:
                    errors: ["Profile refund saver settings must have either consent or custom_preference or both"]
                    error_code: "validation_error"
                    status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/sdk/purchase/stripe/token/validate/:
    post:
      summary: Valider un achat Stripe
      description: |
        Valide un achat à l'aide du jeton Stripe fourni en utilisant les identifiants Stripe dans les paramètres de votre application dans l'Adapty Dashboard.
        Si l'achat est valide, l'historique des transactions est importé depuis Stripe vers le profil dans Adapty avec le customer_user_id spécifié.
        Si aucun profil avec ce customer_user_id n'existait auparavant, il sera créé.

        Des événements de profil sont générés au cours du processus et les transactions importées sont comptabilisées dans le MTR.
      operationId: validateStripePurchase
      tags:
        - Stripe
      security:
        - apikeyAuth: []
      requestBody:
        required: true
        content:
          application/vnd.api+json:
            schema:
              $ref: '#/components/schemas/StripeValidationRequest'
            example:
              data:
                type: "stripe_receipt_validation_result"
                attributes:
                  customer_user_id: "<YOUR_CUSTOMER_USER_ID>"
                  stripe_token: "<YOUR_STRIPE_TOKEN>"
      responses:
        '200':
          description: Achat validé avec succès
          content:
            application/vnd.api+json:
              schema:
                $ref: '#/components/schemas/StripeValidationResponse'
              example:
                data: null
        '400':
          description: Requête incorrecte
          content:
            application/vnd.api+json:
              schema:
                $ref: '#/components/schemas/StripeErrorResponse'
              example:
                errors:
                  - detail: "none is not an allowed value"
                    source:
                      pointer: "/data/attributes/stripe_token"
                    status: "400"
        '401':
          description: Non autorisé
          content:
            application/vnd.api+json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
          content:
            application/vnd.api+json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/server-side-api/purchase/paddle/token/validate/:
    post:
      summary: Valider un achat Paddle
      description: |
        Valide un achat à l'aide du jeton Paddle fourni en utilisant les identifiants Paddle dans les paramètres de votre application dans l'Adapty Dashboard.
        Si l'achat est valide, l'historique des transactions est importé depuis Paddle vers le profil dans Adapty avec le customer_user_id spécifié.
        Si aucun profil avec ce customer_user_id n'existait auparavant, il sera créé.
      operationId: validatePaddlePurchase
      tags:
        - Paddle
      security:
        - apikeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaddleValidationRequest'
            example:
              customer_user_id: "<YOUR_CUSTOMER_USER_ID>"
              paddle_token: "live_7d279f61a3339fed520f7cd8c08"
      responses:
        '200':
          description: Achat validé avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileResponse'
              example:
                data:
                  app_id: "14c3d623-2f3a-455a-aa86-ef83dff6913b"
                  profile_id: "3286abd3-48b0-4e9c-a5f6-ac0a006804a6"
                  customer_user_id: "Jane.doe@example.com"
                  total_revenue_usd: 0.0
                  segment_hash: "8f45947bad31ab0c"
                  timestamp: 1736436751469
                  custom_attributes:
                    - key: "favourite_sport"
                      value: "yoga"
                  access_levels: []
                  subscriptions:
                    - purchase_id: "5a7ab471-2299-45f7-ad69-1d395c1256e3"
                      store: "app_store"
                      store_product_id: "1year.premium"
                      store_base_plan_id: null
                      store_transaction_id: "30002109551456"
                      store_original_transaction_id: "30002109551456"
                      purchased_at: "2022-10-12T09:42:50+00:00"
                      environment: "Production"
                      is_refund: false
                      is_consumable: false
                  non_subscriptions: []
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                no_products_found:
                  summary: Aucun produit trouvé
                  value:
                    errors: ["No products found"]
                    error_code: "no_products_found"
                    status_code: 400
                paddle_api_key_not_found:
                  summary: Clé API Paddle introuvable
                  value:
                    errors: ["Paddle API key not found"]
                    error_code: "paddle_api_key_not_found"
                    status_code: 400
                invalid_paddle_credentials_or_purchase_not_found:
                  summary: Identifiants Paddle invalides ou achat introuvable
                  value:
                    errors: ["Invalid Paddle credentials or purchase not found"]
                    error_code: "invalid_paddle_credentials_or_purchase_not_found"
                    status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/server-side-api/integration/profile/set/integration-identifiers/:
    get:
      summary: Obtenir les identifiants d'intégration
      description: Récupère les identifiants d'intégration d'un profil.
      operationId: getIntegrationIdentifiers
      tags:
        - Integration
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Identifiants d'intégration récupérés avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntegrationIdentifiersResponse'
              example:
                facebook_anonymous_id: "XZ7EF7D15E-8FA1-49D8-B180-918EB333E42A"
                amplitude_user_id: null
                amplitude_device_id: null
                mixpanel_user_id: "33w6yv5DPqVlyMVbjW31xvzJLtJ3"
                appmetrica_profile_id: null
                appmetrica_device_id: null
                one_signal_player_id: null
                one_signal_subscription_id: "333ed338-757d-466a-a672-ab92db196a1f"
                pushwoosh_hwid: null
                firebase_app_instance_id: "C333B35DF1DB418E99F7B815E9F5C549"
                airbridge_device_id: null
                appsflyer_id: "1741933337626-3179568"
                branch_id: null
                adjust_device_id: null
                tenjin_analytics_installation_id: null
                posthog_distinct_user_id: null
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Profile not found"]
                error_code: "profile_not_found"
                status_code: 404
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      summary: Définir les identifiants d'intégration
      description: Ajoute des identifiants d'intégration à un profil.
      operationId: setIntegrationIdentifiers
      tags:
        - Integration
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IntegrationIdentifiersRequest'
            example:
              pushwoosh_hwid: "example_pushwoosh_hwid"
              mixpanel_user_id: "example_mixpanel_user_id"
              facebook_anonymous_id: "example_facebook_anonymous_id"
              firebase_app_instance_id: "example_firebase_app_instance_id"
              amplitude_user_id: "example_amplitude_user_id"
              amplitude_device_id: "example_amplitude_device_id"
              appmetrica_device_id: "example_appmetrica_device_id"
              appmetrica_profile_id: "example_appmetrica_profile_id"
              one_signal_subscription_id: "example_one_signal_subscription_id"
              one_signal_player_id: "example_one_signal_player_id"
              branch_id: "example_branch_id"
              appsflyer_id: "example_appsflyer_id"
              adjust_device_id: "example_adjust_device_id"
              airbridge_device_id: "example_airbridge_device_id"
              tenjin_analytics_installation_id: "example_tenjin_analytics_installation_id"
              posthog_distinct_user_id: "example_posthog_distinct_user_id"
      responses:
        '200':
          description: Identifiants d'intégration définis avec succès. Le corps de la réponse est vide.
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["At least one integration identifier must be provided."]
                error_code: "validation_error"
                status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '404':
          description: Profil introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Profile not found"]
                error_code: "profile_not_found"
                status_code: 404
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/server-side-api/paywalls/:
    get:
      summary: Lister les paywalls
      description: Récupère la liste des paywalls de votre application.
      operationId: listPaywalls
      tags:
        - Paywalls
      security:
        - apikeyAuth: []
      parameters:
        - name: page[number]
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numéro de page pour la pagination
        - name: page[size]
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Nombre d'éléments par page
      responses:
        '200':
          description: Paywalls récupérés avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaywallListResponse'
              example:
                data:
                  - title: "LlkTlizT"
                    paywall_id: "fd891d4f-5906-45b9-97c1-13cc3dc665df"
                    use_paywall_builder: false
                    use_paywall_builder_legacy: false
                    updated_at: "2025-07-08T07:27:06.754527+00:00"
                    created_at: "2025-07-08T07:27:06.754541+00:00"
                    state: "live"
                    is_deleted: false
                    web_purchase_url: null
                    products:
                      - product_id: "b95e9e51-a056-4eb6-9cf7-b75d139e7c3c"
                        title: "mFUQPcJQ"
                        product_set: "uncategorised"
                        offer: null
                  - title: "Premium Subscription"
                    paywall_id: "a1cf7850-1bb8-4151-8336-a4e588730c55"
                    use_paywall_builder: true
                    use_paywall_builder_legacy: false
                    updated_at: "2025-07-28T08:15:13.722680+00:00"
                    created_at: "2025-07-25T13:40:01.789853+00:00"
                    state: "live"
                    is_deleted: false
                    web_purchase_url: "https://example.com/purchase"
                    products:
                      - product_id: "b136422f-8153-402a-afbb-986929c68f6a"
                        title: "Premium Monthly"
                        product_set: "uncategorised"
                        offer:
                          product_offer_id: "e31a4296-f250-4faf-ac80-3cc93c2da8f5"
                          title: "Free Trial"
                meta:
                  pagination:
                    count: 365
                    page: 1
                    pages: 183
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: "page_size"
                    errors: ["Invalid page_size parameter. Must be between 1 and 100."]
                error_code: "validation_error"
                status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/server-side-api/paywalls/{paywall_id}/:
    get:
      summary: Obtenir un paywall
      description: Récupère un paywall spécifique de votre application.
      operationId: getPaywall
      tags:
        - Paywalls
      security:
        - apikeyAuth: []
      parameters:
        - name: paywall_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du paywall à récupérer
      responses:
        '200':
          description: Paywall récupéré avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaywallResponse'
              example:
                title: "Premium Subscription"
                paywall_id: "fd891d4f-5906-45b9-97c1-13cc3dc665df"
                use_paywall_builder: true
                use_paywall_builder_legacy: false
                updated_at: "2025-07-30T11:13:58.798Z"
                created_at: "2025-07-30T11:13:58.798Z"
                state: "live"
                is_deleted: false
                web_purchase_url: "https://example.com/purchase"
                products:
                  - product_id: "b95e9e51-a056-4eb6-9cf7-b75d139e7c3c"
                    title: "Premium Monthly"
                    product_set: "uncategorised"
                    offer:
                      product_offer_id: "e31a4296-f250-4faf-ac80-3cc93c2da8f5"
                      title: "Free Trial"
                remote_configs:
                  - locale: "en"
                    data: "{\"title\":\"Premium Features\",\"subtitle\":\"Unlock all premium content\"}"
                main_screenshot:
                  image_id: 123456
                  url: "https://public-media.adapty.io/public/screenshot.jpg"
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '404':
          description: Paywall introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: null
                    errors: ["Paywall not found"]
                error_code: "paywall_does_not_exist"
                status_code: 404
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      summary: Mettre à jour un paywall
      description: |
        Met à jour le Remote Config d'un paywall spécifique. Ce point de terminaison vous permet de modifier les valeurs du Remote Config qui vous aident à personnaliser l'apparence et le comportement du paywall.

        **Important :** Si vous mettez à jour un Remote Config, il écrasera tous les Remote Configs existants ! Si vous devez conserver les Remote Configs existants, commencez par récupérer le paywall. Copiez ensuite les remote_configs depuis celui-ci et modifiez les objets nécessaires dans la requête de mise à jour.
      operationId: updatePaywall
      tags:
        - Paywalls
      security:
        - apikeyAuth: []
      parameters:
        - name: paywall_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: L'identifiant unique du paywall à mettre à jour
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaywallUpdateRequest'
            example:
              remote_configs:
                - locale: "en"
                  data: "{\"title\":\"Premium Features\",\"subtitle\":\"Unlock all premium content\"}"
      responses:
        '200':
          description: Paywall mis à jour avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaywallResponse'
              example:
                title: "Premium Subscription"
                paywall_id: "fd891d4f-5906-45b9-97c1-13cc3dc665df"
                use_paywall_builder: true
                use_paywall_builder_legacy: false
                updated_at: "2025-07-30T11:13:58.798Z"
                created_at: "2025-07-30T11:13:58.798Z"
                state: "live"
                is_deleted: false
                web_purchase_url: "https://example.com/purchase"
                products:
                  - product_id: "b95e9e51-a056-4eb6-9cf7-b75d139e7c3c"
                    title: "Premium Monthly"
                    product_set: "uncategorised"
                    offer:
                      product_offer_id: "e31a4296-f250-4faf-ac80-3cc93c2da8f5"
                      title: "Free Trial"
                remote_configs:
                  - locale: "en"
                    data: "{\"title\":\"Premium Features\",\"subtitle\":\"Unlock all premium content\"}"
                main_screenshot:
                  image_id: 123456
                  url: "https://public-media.adapty.io/public/screenshot.jpg"
        '400':
          description: Requête incorrecte
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: "remote_configs"
                    errors: ["At least one field must be provided"]
                error_code: "validation_error"
                status_code: 400
        '401':
          description: Non autorisé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors: ["Invalid API key"]
                error_code: "unauthorized"
                status_code: 401
        '404':
          description: Paywall introuvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: null
                    errors: ["Paywall not found"]
                error_code: "paywall_does_not_exist"
                status_code: 404
        '500':
          description: Erreur interne du serveur
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/server-side-api/vc/transactions/:
    post:
      summary: Créer une transaction de monnaie virtuelle
      description: |
        Crédite ou débite une ou plusieurs monnaies virtuelles pour un profil en une seule transaction atomique. Utilisez un `amount` positif pour créditer (accorder) et un `amount` négatif pour débiter (dépenser). Tous les éléments sont appliqués ensemble — si l'un échoue, aucun n'est appliqué.

        Chaque code de monnaie ne peut apparaître qu'une seule fois par requête. Un crédit via ce point de terminaison crée toujours un solde sans expiration.
      operationId: createVirtualCurrencyTransaction
      tags:
        - Virtual Currency
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: |
            Un UUID v4 qui rend la requête idempotente. Si vous relancez une requête avec la même clé, Adapty renvoie le résultat de la transaction originale sans modifier à nouveau les soldes. Doit être un UUID v4 valide.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VirtualCurrencyTransactionRequest'
            examples:
              debit:
                summary: Dépenser (débiter) une monnaie
                value:
                  items:
                    - currency_code: "COINS"
                      amount: -10
              credit:
                summary: Accorder (créditer) une monnaie
                value:
                  items:
                    - currency_code: "COINS"
                      amount: 500
              multi_currency:
                summary: Débiter deux monnaies simultanément
                value:
                  items:
                    - currency_code: "GOLD"
                      amount: -50
                    - currency_code: "SILVER"
                      amount: -200
              conversion:
                summary: Convertir une monnaie en une autre
                value:
                  items:
                    - currency_code: "SILVER"
                      amount: -100
                    - currency_code: "GOLD"
                      amount: 10
                  metadata:
                    reason: "exchange"
      responses:
        '200':
          description: Transaction appliquée avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VirtualCurrencyTransactionResponse'
              example:
                transaction_id: "0190e8a4-1c2b-7def-8abc-2c1a4b6d8e90"
                balances:
                  - code: "COINS"
                    name: "Gold Coins"
                    balance: 12950
                    held: 0
                    available: 12950
        '400':
          description: |
            Requête incorrecte. Le champ `error_code` identifie la cause : `insufficient_balance`, `unknown_currency`, `duplicate_currency`, `amount_zero`, `balance_overflow`, `empty_items`, ou `idempotency_key_invalid`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: "currency_code"
                    errors: ["Insufficient balance for currency COINS"]
                error_code: "insufficient_balance"
                status_code: 400
        '401':
          description: Non autorisé. La clé API est manquante ou invalide.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Interdit. L'API serveur pour les monnaies virtuelles n'est pas activée pour cette application. Contactez le support Adapty pour en demander l'accès.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: null
                    errors: ["Server API for virtual currencies is not enabled for this app. Contact Adapty support to request access."]
                error_code: "feature_not_enabled"
                status_code: 403
        '404':
          description: Profil introuvable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: |
            Conflit. Le champ `error_code` identifie la cause :

            - `idempotency_in_flight` — une requête avec la même `Idempotency-Key` est toujours en cours de traitement. Réessayez après l'intervalle indiqué dans l'en-tête de réponse `Retry-After`.
            - `duplicate_source_transaction` — la transaction du store sous-jacente a déjà été créditée, elle n'est donc pas appliquée à nouveau.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                errors:
                  - source: null
                    errors: ["A similar request is being processed."]
                error_code: "idempotency_in_flight"
                status_code: 409
        '422':
          description: |
            Entité non traitable. Le corps de la requête n'a pas passé la validation du schéma — par exemple, `items` contient plus de 20 entrées, ou une clé ou valeur de `metadata` ne respecte pas ses contraintes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: |
            Trop de requêtes. La limite de débit par application ou globale a été dépassée (`error_code` est `rate_limited`). Les limites par défaut sont 600 requêtes par minute par application et 6 000 requêtes par minute globalement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur
    get:
      summary: Lister les transactions de monnaie virtuelle
      description: |
        Renvoie l'historique des transactions de monnaie virtuelle pour un profil, de la plus récente à la plus ancienne. Les résultats utilisent la pagination par curseur : lorsque la réponse inclut un `pagination.next_cursor` non nul, transmettez-le en tant que paramètre de requête `cursor` pour récupérer la page suivante.
      operationId: listVirtualCurrencyTransactions
      tags:
        - Virtual Currency
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: currency_code
          in: query
          required: false
          schema:
            type: string
          description: Filtrer l'historique sur un seul code de monnaie virtuelle.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Curseur de pagination opaque renvoyé en tant que `pagination.next_cursor` dans une réponse précédente. Omettez ce paramètre pour récupérer la première page.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
          description: Nombre maximum de transactions à retourner par page. Par défaut 50.
      responses:
        '200':
          description: Historique des transactions retourné avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VirtualCurrencyTransactionsHistoryResponse'
              example:
                data:
                  - transaction_id: "0190e8a4-1c2b-7def-8abc-2c1a4b6d8e90"
                    transaction_type: "api_transaction"
                    items:
                      - currency_code: "COINS"
                        amount: 500
                        balance_after: 12950
                    metadata:
                      reason: "promo_bonus"
                    created_at: "2026-05-23T10:30:00Z"
                  - transaction_id: "0190e7b2-9a44-7c10-bb31-7d2e9f0a1c55"
                    transaction_type: "store_purchase"
                    items:
                      - currency_code: "COINS"
                        amount: 1000
                        balance_after: 12450
                    created_at: "2026-05-22T18:05:12Z"
                  - transaction_id: "0190e5c0-3f18-7b9a-9e6d-44a1c8b27e03"
                    transaction_type: "subscription_renewal"
                    items:
                      - currency_code: "GEMS"
                        amount: 200
                        balance_after: 3280
                    created_at: "2026-05-20T09:00:00Z"
                pagination:
                  next_cursor: "MjAyNi0wNS0yMFQwOTowMDowMFowMTkwZTVjMC0zZjE4LTdiOWEtOWU2ZC00NGExYzhiMjdlMDM="
        '400':
          description: Requête incorrecte. La valeur du `cursor` est invalide (`error_code` est `invalid_cursor`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé. La clé API est manquante ou invalide.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Interdit. L'API serveur pour les monnaies virtuelles n'est pas activée pour cette application.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur

  /api/v2/server-side-api/vc/balances/:
    get:
      summary: Lister les soldes de monnaies virtuelles
      description: |
        Renvoie le solde actuel de chaque monnaie virtuelle pour un profil. Si l'application n'a pas de monnaies virtuelles, `data` est un tableau vide.
      operationId: listVirtualCurrencyBalances
      tags:
        - Virtual Currency
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du client dans votre système. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: L'identifiant unique du profil dans votre système. Meilleure option si vous travaillez avec des profils anonymes. `adapty-customer-user-id` ou `adapty-profile-id` est requis.
      responses:
        '200':
          description: Soldes retournés avec succès
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VirtualCurrencyBalanceListResponse'
              example:
                response_created_at: 1779791400000
                data:
                  - code: "COINS"
                    name: "Gold Coins"
                    balance: 12450
                    held: 0
                    available: 12450
                  - code: "GEMS"
                    name: "Gems"
                    balance: 3280
                    held: 0
                    available: 3280
                  - code: "ENERGY"
                    name: "Energy"
                    balance: 840
                    held: 0
                    available: 840
        '400':
          description: Requête incorrecte.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Non autorisé. La clé API est manquante ou invalide.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Interdit. L'API serveur pour les monnaies virtuelles n'est pas activée pour cette application.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Profil introuvable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Erreur interne du serveur

components:
  securitySchemes:
    apikeyAuth:
      type: apiKey
      name: Authorization
      in: header
      default: Api-Key {Your secret API key}
      description: |
        Les requêtes API doivent être authentifiées par votre clé API secrète via l'en-tête **Authorization** 
        avec la valeur `Api-Key {your_secret_api_key}`, par exemple, 
        `Api-Key secret_live_...`. Retrouvez cette clé dans l'Adapty Dashboard -> 
        **App Settings** -> onglet **General** -> section **API keys**.
  requestBodies:
    DefaultJson:
      required: true
      content:
        application/json:
          schema:
            type: object
  schemas:
    ProfileRequest:
      type: object
      properties:
        first_name:
          type: string
          nullable: true
          description: Prénom de votre utilisateur final
        last_name:
          type: string
          nullable: true
          description: Nom de famille de votre utilisateur final
        gender:
          type: string
          nullable: true
          enum: [f, m, o]
          description: Genre de votre utilisateur final
        email:
          type: string
          nullable: true
          format: email
          description: Adresse e-mail de votre utilisateur final
        phone_number:
          type: string
          nullable: true
          description: Numéro de téléphone de votre utilisateur final
        birthday:
          type: string
          format: date
          nullable: true
          description: Date de naissance de votre utilisateur final
        ip_country:
          type: string
          nullable: true
          description: Pays de l'utilisateur final au format ISO 3166-2
        ip_v4_address:
          type: string
          nullable: true
          description: Adresse IPv4 de l'utilisateur final
        store_country:
          type: string
          nullable: true
          description: Pays du store de l'utilisateur final
        store:
          type: string
          nullable: true
          enum: [app_store, play_store, stripe, adapty, paddle]
          description: La plateforme utilisée par l'utilisateur pour effectuer des achats dans votre application
        store_account_token:
          type: string
          format: uuid
          nullable: true
          description: Jeton de compte du store
        att_status:
          type: integer
          nullable: true
          enum: [0, 1, 2, 3]
          description: Statut Apple App Tracking Transparency (0=non déterminé, 1=restreint, 2=refusé, 3=autorisé)
        analytics_disabled:
          type: boolean
          nullable: true
          description: Option pour se désinscrire des analyses externes
        custom_attributes:
          type: array
          items:
            $ref: '#/components/schemas/CustomAttribute'
          description: Permet de définir jusqu'à 30 attributs personnalisés pour le profil
        installation_meta:
          $ref: '#/components/schemas/InstallationMeta'
      required:
        - installation_meta
      example:
        first_name: "Jane"
        last_name: "Doe"
        gender: "f"
        email: "jane.doe@example.com"
        phone_number: "+1234567890"
        birthday: "2000-12-31"
        ip_country: "FR"
        ip_v4_address: "192.168.1.1"
        store_country: "US"
        store: "app_store"
        store_account_token: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        att_status: 3
        analytics_disabled: true
        custom_attributes:
          - key: "favourite_sport"
            value: "yoga"
        installation_meta:
          device_id: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
          device: "iPhone 15 Pro"
          locale: "en"
          os: "iOS 17.1"
          platform: "iOS"
          timezone: "Europe/Rome"
          user_agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1"
          app_build: "1.0.0"
          app_version: "1.0.0"
          adapty_sdk_version: "2.0.0"
          idfa: "EA7583CD-A667-48BC-B806-42ECB2B48333"
          idfv: "E9D48DA5-3930-4B41-8521-D953AECD2F33"
          advertising_id: ""
          android_id: ""
          android_app_set_id: ""

    ProfileResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/Profile'
      required:
        - data

    Profile:
      type: object
      properties:
        app_id:
          type: string
          format: uuid
          description: L'identifiant interne de votre application
        profile_id:
          type: string
          format: uuid
          description: Identifiant de profil Adapty
        customer_user_id:
          type: string
          nullable: true
          description: L'identifiant de votre utilisateur dans votre système
        total_revenue_usd:
          type: number
          format: float
          description: Valeur décimale représentant le chiffre d'affaires total en USD généré par le profil
        segment_hash:
          type: string
          description: Paramètre interne
        timestamp:
          type: integer
          format: int64
          description: Temps de réponse en millisecondes, nécessaire pour résoudre une condition de concurrence
        custom_attributes:
          type: array
          items:
            $ref: '#/components/schemas/CustomAttribute'
          description: Maximum de 30 attributs personnalisés autorisés pour le profil
        access_levels:
          type: array
          items:
            $ref: '#/components/schemas/AccessLevel'
          description: Tableau d'objets de niveau d'accès. Tableau vide si le client n'a aucun niveau d'accès
        subscriptions:
          type: array
          items:
            $ref: '#/components/schemas/Subscription'
          description: Tableau d'objets d'abonnement. Tableau vide si le client n'a aucun abonnement
        non_subscriptions:
          type: array
          items:
            $ref: '#/components/schemas/NonSubscription'
          description: Tableau d'objets hors abonnement. Tableau vide si le client n'a aucun achat
      required:
        - app_id
        - profile_id
        - customer_user_id
        - total_revenue_usd
        - segment_hash
        - timestamp
        - custom_attributes
        - access_levels
        - subscriptions
        - non_subscriptions

    CustomAttribute:
      type: object
      properties:
        key:
          type: string
          maxLength: 30
          description: La clé doit être une chaîne de caractères d'au plus 30 caractères. Seuls les lettres, chiffres, tirets, points et underscores sont autorisés
        value:
          oneOf:
            - type: string
            - type: number
          description: La valeur de l'attribut doit comporter au plus 50 caractères. Seules les chaînes de caractères et les décimaux sont autorisés comme valeurs
      required:
        - key
        - value

    InstallationMeta:
      type: object
      properties:
        device_id:
          type: string
          format: uuid
          description: Identifiant unique de l'appareil
        device:
          type: string
          nullable: true
          description: Informations sur l'appareil
        locale:
          type: string
          nullable: true
          description: Langue de l'appareil
        os:
          type: string
          nullable: true
          description: Informations sur le système d'exploitation
        platform:
          type: string
          nullable: true
          enum: [iOS, macOS, iPadOS, Android, visionOS, web]
          description: Plateforme (iOS, Android, etc.)
        timezone:
          type: string
          nullable: true
          description: Fuseau horaire de l'appareil
        user_agent:
          type: string
          nullable: true
          description: Chaîne user agent
        app_build:
          type: string
          nullable: true
          description: Version de build de l'application
        app_version:
          type: string
          nullable: true
          description: Version de l'application
        adapty_sdk_version:
          type: string
          nullable: true
          description: Version du SDK Adapty
        idfa:
          type: string
          nullable: true
          description: Identifiant iOS pour les annonceurs
        idfv:
          type: string
          nullable: true
          description: Identifiant iOS pour les éditeurs
        advertising_id:
          type: string
          nullable: true
          description: Identifiant publicitaire Android
        android_id:
          type: string
          nullable: true
          description: Identifiant de l'appareil Android
        android_app_set_id:
          type: string
          nullable: true
          description: Identifiant d'ensemble d'applications Android
      required:
        - device_id

    AccessLevel:
      type: object
      properties:
        access_level_id:
          type: string
          description: Identifiant du niveau d'accès
        store:
          type: string
          description: Store où le niveau d'accès a été acheté
        store_product_id:
          type: string
          description: Identifiant du produit dans le store
        store_base_plan_id:
          type: string
          nullable: true
          description: Identifiant du plan de base dans le store
        store_transaction_id:
          type: string
          description: Identifiant de transaction dans le store
        store_original_transaction_id:
          type: string
          description: Identifiant de transaction originale dans le store
        offer:
          allOf:
            - $ref: '#/components/schemas/OfferDTO'
          nullable: true
          description: Détails de l'offre, si une offre promotionnelle ou de lancement a été appliquée
        starts_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le niveau d'accès commence
        purchased_at:
          type: string
          format: date-time
          description: Quand le niveau d'accès a été acheté
        originally_purchased_at:
          type: string
          format: date-time
          description: Quand le niveau d'accès a été acheté initialement
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le niveau d'accès expire
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le renouvellement a été annulé
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: Quand un problème de facturation a été détecté
        is_in_grace_period:
          type: boolean
          description: Si le niveau d'accès est en délai de grâce
        cancellation_reason:
          type: string
          nullable: true
          description: Raison de l'annulation

    Subscription:
      type: object
      properties:
        store:
          type: string
          description: Store où l'abonnement a été acheté
        store_product_id:
          type: string
          description: Identifiant du produit dans le store
        store_base_plan_id:
          type: string
          nullable: true
          description: Identifiant du plan de base dans le store
        store_transaction_id:
          type: string
          description: Identifiant de transaction dans le store
        store_original_transaction_id:
          type: string
          description: Identifiant de transaction originale dans le store
        offer:
          allOf:
            - $ref: '#/components/schemas/OfferDTO'
          nullable: true
          description: Détails de l'offre, si une offre promotionnelle ou de lancement a été appliquée
        environment:
          type: string
          description: Environnement (Sandbox, Production)
        purchased_at:
          type: string
          format: date-time
          description: Quand l'abonnement a été acheté
        originally_purchased_at:
          type: string
          format: date-time
          description: Quand l'abonnement a été acheté initialement
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: Quand l'abonnement expire
        renewal_cancelled_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le renouvellement a été annulé
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: Quand un problème de facturation a été détecté
        is_in_grace_period:
          type: boolean
          description: Si l'abonnement est en délai de grâce
        cancellation_reason:
          type: string
          nullable: true
          description: Raison de l'annulation

    NonSubscription:
      type: object
      properties:
        purchase_id:
          type: string
          format: uuid
          description: Identifiant unique de l'achat
        store:
          type: string
          description: Store où l'achat a été effectué
        store_product_id:
          type: string
          description: Identifiant du produit dans le store
        store_base_plan_id:
          type: string
          nullable: true
          description: Identifiant du plan de base dans le store
        store_transaction_id:
          type: string
          description: Identifiant de transaction dans le store
        store_original_transaction_id:
          type: string
          description: Identifiant de transaction originale dans le store
        purchased_at:
          type: string
          format: date-time
          description: Quand l'achat a été effectué
        environment:
          type: string
          description: Environnement (Sandbox, Production)
        is_refund:
          type: boolean
          description: Si cet achat est un remboursement
        is_consumable:
          type: boolean
          description: Si cet achat est un consommable

    ErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              source:
                type: string
                nullable: true
                description: Source de l'erreur
              errors:
                type: array
                items:
                  type: string
                description: Tableau de messages d'erreur
        error_code:
          type: string
          description: Nom court de l'erreur
        status_code:
          type: integer
          description: Code de statut HTTP
      required:
        - errors
        - error_code
        - status_code

    TransactionDTORequest:
      oneOf:
        - $ref: '#/components/schemas/OneTimePurchaseDTO'
        - $ref: '#/components/schemas/SubscriptionDTO'
      discriminator:
        propertyName: purchase_type

    OneTimePurchaseDTO:
      type: object
      title: Achat unique
      description: Données de transaction pour un achat unique
      properties:
        purchase_type:
          type: string
          enum: [one_time_purchase]
          description: Type d'achat
        store:
          type: string
          description: Store où l'achat a été effectué. Les valeurs courantes sont `app_store`, `play_store`, `stripe`, `paddle`, ou tout identifiant de store personnalisé
        environment:
          type: string
          enum: [Production, Sandbox]
          default: Production
          description: Environnement où l'achat a été effectué
        store_product_id:
          type: string
          description: Identifiant du produit dans le store
        store_transaction_id:
          type: string
          maxLength: 50
          description: Identifiant de transaction dans le store. Maximum 50 caractères. Les identifiants de transaction PayPal et de certains stores personnalisés peuvent dépasser cette limite — tronquez ou hachez la valeur avant de la soumettre, sinon la requête échoue.
        store_original_transaction_id:
          type: string
          description: Identifiant de transaction originale dans le store
        is_family_shared:
          type: boolean
          default: false
          description: Si l'achat est partagé en famille
        price:
          $ref: '#/components/schemas/PriceDTO'
        purchased_at:
          type: string
          format: date-time
          description: Quand l'achat a été effectué
        variation_id:
          type: string
          nullable: true
          description: Identifiant de variante pour les tests A/B
        offer:
          $ref: '#/components/schemas/OfferDTO'
          nullable: true
        refunded_at:
          type: string
          format: date-time
          nullable: true
          description: Quand l'achat a été remboursé
        cancellation_reason:
          type: string
          enum: [billing_error, cancelled_by_developer, new_subscription_replace, price_increase, product_was_not_available, refund, unknown, upgraded, voluntarily_cancelled, adapty_revoked]
          nullable: true
          description: Raison de l'annulation
      required:
        - purchase_type
        - store
        - store_product_id
        - store_transaction_id
        - store_original_transaction_id
        - price
        - purchased_at

    SubscriptionDTO:
      type: object
      title: Abonnement
      description: Données de transaction pour un achat par abonnement
      properties:
        purchase_type:
          type: string
          enum: [subscription]
          description: Type d'achat
        store:
          type: string
          description: Store où l'achat a été effectué. Les valeurs courantes sont `app_store`, `play_store`, `stripe`, `paddle`, ou tout identifiant de store personnalisé
        environment:
          type: string
          enum: [Production, Sandbox]
          default: Production
          description: Environnement où l'achat a été effectué
        store_product_id:
          type: string
          description: Identifiant du produit dans le store
        store_transaction_id:
          type: string
          maxLength: 50
          description: Identifiant de transaction dans le store. Maximum 50 caractères. Les identifiants de transaction PayPal et de certains stores personnalisés peuvent dépasser cette limite — tronquez ou hachez la valeur avant de la soumettre, sinon la requête échoue.
        store_original_transaction_id:
          type: string
          description: Identifiant de transaction originale dans le store
        is_family_shared:
          type: boolean
          default: false
          description: Si l'achat est partagé en famille
        price:
          $ref: '#/components/schemas/PriceDTO'
        purchased_at:
          type: string
          format: date-time
          description: Quand l'achat a été effectué
        variation_id:
          type: string
          nullable: true
          description: Identifiant de variante pour les tests A/B
        offer:
          $ref: '#/components/schemas/OfferDTO'
          nullable: true
        refunded_at:
          type: string
          format: date-time
          nullable: true
          description: Quand l'achat a été remboursé
        cancellation_reason:
          type: string
          enum: [billing_error, cancelled_by_developer, new_subscription_replace, price_increase, product_was_not_available, refund, unknown, upgraded, voluntarily_cancelled, adapty_revoked]
          nullable: true
          description: Raison de l'annulation
        originally_purchased_at:
          type: string
          format: date-time
          description: Quand l'abonnement a été acheté initialement
        expires_at:
          type: string
          format: date-time
          description: Quand l'abonnement expire
        renew_status:
          type: boolean
          description: Si l'abonnement sera renouvelé
        renew_status_changed_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le statut de renouvellement a été modifié
        billing_issue_detected_at:
          type: string
          format: date-time
          nullable: true
          description: Quand un problème de facturation a été détecté
        grace_period_expires_at:
          type: string
          format: date-time
          nullable: true
          description: Quand le délai de grâce expire
      required:
        - purchase_type
        - store
        - store_product_id
        - store_transaction_id
        - store_original_transaction_id
        - price
        - purchased_at
        - originally_purchased_at
        - expires_at
        - renew_status

    PriceDTO:
      type: object
      properties:
        country:
          type: string
          description: Code pays
        currency:
          type: string
          description: Code devise
        value:
          type: number
          format: float
          description: Valeur du prix
      required:
        - country
        - currency
        - value

    OfferDTO:
      type: object
      properties:
        category:
          type: string
          enum: [introductory, promotional, offer_code, win_back]
          description: Catégorie de l'offre
        type:
          type: string
          enum: [free_trial, pay_as_you_go, pay_up_front]
          description: Type d'offre
        id:
          type: string
          nullable: true
          description: Identifiant de l'offre
      required:
        - category
        - type

    GrantAccessRequest:
      type: object
      properties:
        access_level_id:
          type: string
          description: Identifiant du niveau d'accès payant configuré dans la page des niveaux d'accès
        starts_at:
          type: string
          format: date-time
          nullable: true
          description: La date et l'heure à laquelle le niveau d'accès sera actif. Peut être dans le futur. La valeur par défaut est null.
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: La date et l'heure à laquelle le niveau d'accès expirera. Peut être dans le passé et peut être null pour un accès à vie. La valeur par défaut est null.
      required:
        - access_level_id

    RevokeAccessRequest:
      type: object
      properties:
        access_level_id:
          type: string
          description: Identifiant du niveau d'accès payant configuré dans la page des niveaux d'accès
        revoke_at:
          type: string
          format: date-time
          nullable: true
          description: Indique quand le niveau d'accès expirera. Pour révoquer l'accès immédiatement, omettez ce champ ou définissez-le sur null. La valeur par défaut est null.
      required:
        - access_level_id

    RefundSaverSettingsRequest:
      type: object
      properties:
        custom_preference:
          type: string
          enum: [grant, no_preference, decline]
          nullable: true
          description: |
            Définir la préférence de remboursement individuellement pour l'utilisateur.
            - `grant` : approuver chaque demande de remboursement
            - `no_preference` : ne fournir aucune recommandation
            - `decline` : refuser chaque demande de remboursement
        consent:
          type: boolean
          nullable: true
          description: |
            Enregistrer si l'utilisateur a donné son consentement au partage de ses données.
            - `true` signifie que si vous recevez une demande de remboursement intégré à l'application, vous pouvez fournir à Apple des informations sur l'utilisateur
      example:
        custom_preference: "grant"
        consent: true

    RefundSaverSettingsResponse:
      type: object
      properties:
        profile_id:
          type: string
          format: uuid
          description: Identifiant du profil client
        settings:
          type: object
          properties:
            consent:
              type: boolean
              description: Indique si l'utilisateur a consenti au partage de ses données
            custom_preference:
              type: string
              enum: [grant, no_preference, decline]
              description: La préférence de remboursement
          description: Paramètres Refund Saver pour l'utilisateur
      required:
        - profile_id
        - settings

    StripeValidationRequest:
      type: object
      properties:
        data:
          type: object
          properties:
            type:
              type: string
              enum: [stripe_receipt_validation_result]
              description: Le type de la ressource
            attributes:
              type: object
              properties:
                customer_user_id:
                  type: string
                  description: L'identifiant de votre utilisateur dans votre système
                stripe_token:
                  type: string
                  description: |
                    Jeton d'un objet Stripe représentant un achat unique.
                    Peut être soit un jeton d'abonnement Stripe (sub_XXX), soit un jeton d'intention de paiement (pi_XXX)
              required:
                - customer_user_id
                - stripe_token
          required:
            - type
            - attributes
      required:
        - data

    StripeValidationResponse:
      type: object
      properties:
        data:
          type: object
          nullable: true
          description: Données de réponse (null en cas de validation réussie)
      required:
        - data

    StripeErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              detail:
                type: string
                description: Informations descriptives sur l'erreur
              source:
                type: object
                properties:
                  pointer:
                    type: string
                    description: Référence l'emplacement exact dans le document de requête à l'origine du problème
                required:
                  - pointer
              status:
                type: string
                description: Code de statut HTTP
            required:
              - detail
              - source
              - status
      required:
        - errors

    PaddleValidationRequest:
      type: object
      properties:
        customer_user_id:
          type: string
          description: L'identifiant de votre utilisateur dans votre système
        paddle_token:
          type: string
          description: |
            Jeton d'un objet Paddle représentant un achat unique.
            Peut être soit un identifiant de transaction (txn_...) soit un identifiant d'abonnement (sub_...)
      required:
        - customer_user_id
        - paddle_token

    IntegrationIdentifiersRequest:
      type: object
      properties:
        adjust_device_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration Adjust
        airbridge_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Airbridge
        amplitude_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Amplitude
        amplitude_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Amplitude
        appmetrica_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration AppMetrica
        appmetrica_profile_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration AppMetrica
        appsflyer_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration AppsFlyer
        branch_id:
          type: string
          nullable: true
          description: La clé Branch de l'application de l'utilisateur dans l'intégration Branch
        facebook_anonymous_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Facebook Ads
        firebase_app_instance_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Firebase
        mixpanel_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Mixpanel
        one_signal_player_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant hérité)
        one_signal_subscription_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant recommandé)
        posthog_distinct_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration PostHog
        pushwoosh_hwid:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Pushwoosh
        tenjin_analytics_installation_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Tenjin
      description: Au moins un identifiant d'intégration doit être fourni

    IntegrationIdentifiersResponse:
      type: object
      properties:
        adjust_device_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration Adjust
        airbridge_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Airbridge
        amplitude_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Amplitude
        amplitude_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Amplitude
        appmetrica_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration AppMetrica
        appmetrica_profile_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration AppMetrica
        appsflyer_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration AppsFlyer
        branch_id:
          type: string
          nullable: true
          description: La clé Branch de l'application de l'utilisateur dans l'intégration Branch
        facebook_anonymous_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Facebook Ads
        firebase_app_instance_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Firebase
        mixpanel_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Mixpanel
        one_signal_player_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant hérité)
        one_signal_subscription_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant recommandé)
        posthog_distinct_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration PostHog
        pushwoosh_hwid:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Pushwoosh
        tenjin_analytics_installation_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Tenjin

    PaywallUpdateRequest:
      type: object
      properties:
        remote_configs:
          type: array
          items:
            type: object
            properties:
              locale:
                type: string
                description: La langue du Remote Config (par ex., "en", "es", "fr")
              data:
                type: string
                description: Chaîne JSON contenant les données du Remote Config
            required:
              - locale
              - data
          description: Tableau d'objets RemoteConfig à mettre à jour
      required:
        - remote_configs
      description: Au moins un champ doit être fourni

    PaywallResponse:
      type: object
      properties:
        title:
          type: string
          description: Le nom du paywall, tel que défini dans votre Adapty Dashboard
        paywall_id:
          type: string
          format: uuid
          description: L'identifiant unique du paywall
        use_paywall_builder:
          type: boolean
          description: Si le paywall utilise le Paywall Builder
        use_paywall_builder_legacy:
          type: boolean
          description: Si le paywall utilise le Paywall Builder hérité
        updated_at:
          type: string
          format: date-time
          description: Horodatage de la dernière mise à jour du paywall
        created_at:
          type: string
          format: date-time
          description: Horodatage de la création du paywall
        state:
          type: string
          enum: [draft, live, inactive, archived]
          description: L'état actuel du paywall
        is_deleted:
          type: boolean
          description: Si le paywall est marqué comme supprimé
        web_purchase_url:
          type: string
          format: uri
          nullable: true
          description: URL pour les achats web, le cas échéant
        products:
          type: array
          items:
            type: object
            properties:
              product_id:
                type: string
                format: uuid
                description: L'identifiant unique du produit
              title:
                type: string
                description: Le titre du produit
              product_set:
                type: string
                enum: [weekly, monthly, trimonthly, semiannual, annual, lifetime, uncategorised, nonsubscriptions, two_months, consumable]
                description: La catégorie du groupe de produits
              offer:
                type: object
                nullable: true
                properties:
                  product_offer_id:
                    type: string
                    format: uuid
                    description: L'identifiant unique de l'offre produit
                  title:
                    type: string
                    description: Le titre de l'offre
                required:
                  - product_offer_id
                  - title
            required:
              - product_id
              - title
              - product_set
              - offer
          description: Tableau d'objets produit contenant les informations sur les produits
        remote_configs:
          type: array
          items:
            type: object
            properties:
              locale:
                type: string
                description: La langue du Remote Config
              data:
                type: string
                description: Chaîne JSON contenant les données du Remote Config
            required:
              - locale
              - data
          description: Tableau d'objets RemoteConfig avec la langue et les données
        main_screenshot:
          type: object
          nullable: true
          properties:
            image_id:
              type: integer
              description: L'identifiant unique de l'image
            url:
              type: string
              format: uri
              description: L'URL de l'image
          required:
            - image_id
            - url
          description: Objet capture d'écran principal avec image_id et url
      required:
        - title
        - paywall_id
        - use_paywall_builder
        - use_paywall_builder_legacy
        - updated_at
        - created_at
        - state
        - is_deleted
        - products

    PaywallListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PaywallListItem'
          description: Tableau d'objets paywall
        meta:
          type: object
          properties:
            pagination:
              type: object
              properties:
                count:
                  type: integer
                  description: Nombre total de paywalls
                page:
                  type: integer
                  description: Numéro de page actuel
                pages:
                  type: integer
                  description: Nombre total de pages
              required:
                - count
                - page
                - pages
          required:
            - pagination
      required:
        - data
        - meta

    PaywallListItem:
      type: object
      properties:
        title:
          type: string
          description: Le nom du paywall, tel que défini dans votre Adapty Dashboard
        paywall_id:
          type: string
          format: uuid
          description: L'identifiant unique du paywall
        use_paywall_builder:
          type: boolean
          description: Si le paywall utilise le Paywall Builder
        use_paywall_builder_legacy:
          type: boolean
          description: Si le paywall utilise le Paywall Builder hérité
        updated_at:
          type: string
          format: date-time
          description: Horodatage de la dernière mise à jour du paywall
        created_at:
          type: string
          format: date-time
          description: Horodatage de la création du paywall
        state:
          type: string
          enum: [draft, live, inactive, archived]
          description: L'état actuel du paywall
        is_deleted:
          type: boolean
          description: Si le paywall est marqué comme supprimé
        web_purchase_url:
          type: string
          format: uri
          nullable: true
          description: URL pour les achats web, le cas échéant
        products:
          type: array
          items:
            type: object
            properties:
              product_id:
                type: string
                format: uuid
                description: L'identifiant unique du produit
              title:
                type: string
                description: Le titre du produit
              product_set:
                type: string
                enum: [weekly, monthly, trimonthly, semiannual, annual, lifetime, uncategorised, nonsubscriptions, two_months, consumable]
                description: La catégorie du groupe de produits
              offer:
                type: object
                nullable: true
                properties:
                  product_offer_id:
                    type: string
                    format: uuid
                    description: L'identifiant unique de l'offre produit
                  title:
                    type: string
                    description: Le titre de l'offre
                required:
                  - product_offer_id
                  - title
            required:
              - product_id
              - title
              - product_set
              - offer
          description: Tableau d'objets produit contenant les informations sur les produits du paywall
      required:
        - title
        - paywall_id
        - use_paywall_builder
        - use_paywall_builder_legacy
        - updated_at
        - created_at
        - state
        - is_deleted
        - products

    PaywallObject:
      type: object
      description: |
        Un paywall représente un écran de monétisation dans votre application. Il contient des informations sur 
        la configuration du paywall, les produits et les Remote Configs.
      properties:
        title:
          type: string
          description: Le nom du paywall, tel que défini dans votre Adapty Dashboard
        paywall_id:
          type: string
          format: uuid
          description: L'identifiant unique du paywall
        use_paywall_builder:
          type: boolean
          description: Si le paywall utilise le Paywall Builder
        use_paywall_builder_legacy:
          type: boolean
          description: Si le paywall utilise le Paywall Builder hérité
        updated_at:
          type: string
          format: date-time
          description: Horodatage de la dernière mise à jour du paywall
        created_at:
          type: string
          format: date-time
          description: Horodatage de la création du paywall
        state:
          type: string
          enum: [draft, live, inactive, archived]
          description: L'état actuel du paywall
        is_deleted:
          type: boolean
          description: Si le paywall est marqué comme supprimé
        web_purchase_url:
          type: string
          format: uri
          nullable: true
          description: URL pour les achats web, le cas échéant
        products:
          type: array
          items:
            $ref: '#/components/schemas/ProductObject'
          description: Tableau des produits disponibles dans ce paywall
        remote_configs:
          type: array
          items:
            $ref: '#/components/schemas/RemoteConfigObject'
          description: Tableau des Remote Configs pour les différentes langues
        main_screenshot:
          type: object
          nullable: true
          properties:
            image_id:
              type: integer
              description: L'identifiant unique de l'image
            url:
              type: string
              format: uri
              description: L'URL de l'image
          required: [image_id, url]
          description: Objet capture d'écran principal avec image_id et url
      required:
        - title
        - paywall_id
        - use_paywall_builder
        - use_paywall_builder_legacy
        - updated_at
        - created_at
        - state
        - is_deleted
        - products

    ProductObject:
      type: object
      description: |
        Un produit représente un article pouvant être acheté dans votre application. Il peut s'agir d'un abonnement ou 
        d'un achat unique.
      properties:
        product_id:
          type: string
          format: uuid
          description: L'identifiant unique du produit
        title:
          type: string
          description: Le titre du produit
        product_set:
          type: string
          description: La catégorie du groupe de produits
        offer:
          type: object
          nullable: true
          properties:
            product_offer_id:
              type: string
              format: uuid
              description: L'identifiant unique de l'offre produit
            title:
              type: string
              description: Le titre de l'offre
          required: [product_offer_id, title]
          description: Informations sur l'offre produit
      required:
        - product_id
        - title
        - product_set
        - offer

    RemoteConfigObject:
      type: object
      description: |
        Un Remote Config contient des données de configuration spécifiques à une langue pour un paywall.
        Cela vous permet de personnaliser l'apparence et le comportement du paywall selon les différentes langues.
      properties:
        locale:
          type: string
          description: La langue du Remote Config (par ex., "en", "es", "fr")
        data:
          type: string
          description: Chaîne JSON contenant les données du Remote Config
      required:
        - locale
        - data

    IntegrationIdentifiersObject:
      type: object
      description: |
        Les identifiants d'intégration contiennent les identifiants utilisateur provenant de divers services tiers.
        Ils sont utilisés pour synchroniser les données utilisateur entre différentes plateformes d'analyse et de marketing.
      properties:
        adjust_device_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration Adjust
        airbridge_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Airbridge
        amplitude_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Amplitude
        amplitude_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Amplitude
        appmetrica_device_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration AppMetrica
        appmetrica_profile_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration AppMetrica
        appsflyer_id:
          type: string
          nullable: true
          description: L'identifiant réseau de l'utilisateur dans l'intégration AppsFlyer
        branch_id:
          type: string
          nullable: true
          description: La clé Branch de l'application de l'utilisateur dans l'intégration Branch
        facebook_anonymous_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Facebook Ads
        firebase_app_instance_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Firebase
        mixpanel_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration Mixpanel
        one_signal_player_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant hérité)
        one_signal_subscription_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration OneSignal (identifiant recommandé)
        posthog_distinct_user_id:
          type: string
          nullable: true
          description: L'identifiant de l'utilisateur dans l'intégration PostHog
        pushwoosh_hwid:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Pushwoosh
        tenjin_analytics_installation_id:
          type: string
          nullable: true
          description: L'identifiant de l'appareil de l'utilisateur dans l'intégration Tenjin

    RefundSaverSettingsObject:
      type: object
      description: |
        Les paramètres Refund Saver contiennent les préférences de l'utilisateur pour la gestion des demandes de remboursement.
        Cela inclut le consentement au partage de données et les préférences de remboursement personnalisées.
      properties:
        consent:
          type: boolean
          description: Indique si l'utilisateur a consenti au partage de ses données
        custom_preference:
          type: string
          enum: [grant, no_preference, decline]
          description: La préférence de remboursement
      required:
        - consent
        - custom_preference

    ErrorObject:
      type: object
      description: |
        Une erreur représente une réponse d'erreur API. Elle contient des informations sur 
        ce qui s'est mal passé et comment y remédier.
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              source:
                type: string
                nullable: true
                description: Le champ ou paramètre à l'origine de l'erreur
              errors:
                type: array
                items:
                  type: string
                description: Tableau de messages d'erreur
            required: [errors]
          description: Tableau des détails d'erreur
        error_code:
          type: string
          description: Un code d'erreur lisible par une machine
        status_code:
          type: integer
          description: Le code de statut HTTP
      required:
        - errors
        - error_code
        - status_code

    PaginationObject:
      type: object
      description: |
        Métadonnées de pagination pour les points de terminaison de liste. Contient des informations sur 
        la page actuelle et le total des résultats.
      properties:
        count:
          type: integer
          description: Nombre total d'éléments
        page:
          type: integer
          description: Numéro de page actuel
        pages:
          type: integer
          description: Nombre total de pages
      required:
        - count
        - page
        - pages

    VirtualCurrencyTransactionRequest:
      type: object
      description: Corps de la requête pour créer une transaction de monnaie virtuelle.
      properties:
        items:
          type: array
          minItems: 1
          maxItems: 20
          description: Ajustements de solde à appliquer de manière atomique. Chaque code de monnaie ne peut apparaître qu'une seule fois.
          items:
            $ref: '#/components/schemas/VirtualCurrencyBalanceAdjustment'
        metadata:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: |
            Paires clé-valeur optionnelles stockées avec la transaction. Maximum 5 clés. Chaque clé correspond à `^[a-z0-9_]{1,30}$`, et chaque valeur comporte au plus 200 caractères.
      required:
        - items

    VirtualCurrencyBalanceAdjustment:
      type: object
      properties:
        currency_code:
          type: string
          description: Le code de la monnaie virtuelle à ajuster.
        amount:
          type: integer
          format: int32
          description: |
            Le montant à appliquer. Une valeur positive crédite (accorde) la monnaie ; une valeur négative la débite (dépense). Ne peut pas être nul.
      required:
        - currency_code
        - amount

    VirtualCurrencyTransactionResponse:
      type: object
      description: Résultat d'une transaction de monnaie virtuelle.
      properties:
        transaction_id:
          type: string
          format: uuid
          description: Identifiant unique de la transaction créée.
        balances:
          type: array
          description: |
            Solde après transaction pour chaque monnaie affectée par cette requête (une entrée par élément dans `items`). Pour consulter la liste complète des soldes du profil, utilisez `GET /api/v2/server-side-api/vc/balances/`.
          items:
            $ref: '#/components/schemas/VirtualCurrencyBalanceSnapshot'
      required:
        - transaction_id
        - balances

    VirtualCurrencyBalanceSnapshot:
      type: object
      properties:
        code:
          type: string
          description: Code de la monnaie virtuelle.
        name:
          type: string
          description: Nom d'affichage de la monnaie virtuelle.
        balance:
          type: integer
          format: int32
          minimum: 0
          description: Solde total, y compris les montants actuellement retenus.
        held:
          type: integer
          format: int32
          minimum: 0
          description: Somme de toutes les retenues actives (montants réservés). Actuellement toujours 0.
        available:
          type: integer
          format: int32
          description: Solde disponible à dépenser, calculé comme `balance - held`.
      required:
        - code
        - name
        - balance
        - held
        - available

    VirtualCurrencyBalanceListResponse:
      type: object
      description: Soldes actuels de toutes les monnaies virtuelles d'un profil.
      properties:
        response_created_at:
          type: integer
          format: int64
          description: Moment auquel la réponse a été générée, sous forme d'horodatage Unix en millisecondes.
        data:
          type: array
          items:
            $ref: '#/components/schemas/VirtualCurrencyProfileBalanceItem'
      required:
        - response_created_at
        - data

    VirtualCurrencyProfileBalanceItem:
      type: object
      properties:
        code:
          type: string
          description: Code unique de la monnaie virtuelle dans l'application. Lettres ASCII, chiffres et underscore.
        name:
          type: string
          description: Nom d'affichage de la monnaie virtuelle.
        balance:
          type: integer
          format: int32
          minimum: 0
          description: Solde total, y compris les montants actuellement retenus.
        held:
          type: integer
          format: int32
          minimum: 0
          description: Somme de toutes les retenues actives (montants réservés). Actuellement toujours 0.
        available:
          type: integer
          format: int32
          description: Solde disponible à dépenser, calculé comme `balance - held`.
      required:
        - code
        - name
        - balance
        - held
        - available

    VirtualCurrencyTransactionsHistoryResponse:
      type: object
      description: Liste paginée des transactions de monnaies virtuelles pour un profil.
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/VirtualCurrencyTransactionsHistoryItem'
        pagination:
          $ref: '#/components/schemas/VirtualCurrencyHistoryPagination'
      required:
        - data
        - pagination

    VirtualCurrencyTransactionsHistoryItem:
      type: object
      properties:
        transaction_id:
          type: string
          format: uuid
          description: Identifiant unique de la transaction.
        transaction_type:
          type: string
          description: |
            La source de la transaction. L'une des valeurs suivantes : `store_purchase`, `subscription_renewal`, `trial_start`, `api_transaction`, `dashboard_transaction`, `refund`, ou `expiry`.
        items:
          type: array
          description: Variations de solde par monnaie enregistrées dans cette transaction.
          items:
            $ref: '#/components/schemas/VirtualCurrencyBalanceChange'
        metadata:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: Paires clé-valeur stockées avec la transaction, le cas échéant.
        created_at:
          type: string
          format: date-time
          description: Quand la transaction a été enregistrée.
      required:
        - transaction_id
        - transaction_type
        - items
        - created_at

    VirtualCurrencyBalanceChange:
      type: object
      properties:
        currency_code:
          type: string
          description: Le code de la monnaie virtuelle.
        amount:
          type: integer
          format: int32
          description: La variation signée appliquée au solde. Une valeur positive est un crédit, une valeur négative est un débit.
        balance_after:
          type: integer
          format: int32
          minimum: 0
          description: Le solde de la monnaie après l'application de cette transaction.
      required:
        - currency_code
        - amount
        - balance_after

    VirtualCurrencyHistoryPagination:
      type: object
      properties:
        next_cursor:
          type: string
          nullable: true
          description: |
            Curseur opaque pour la page suivante. Transmettez-le en tant que paramètre de requête `cursor` pour récupérer d'autres résultats. Null lorsqu'il n'y a plus de pages.
      required:
        - next_cursor