> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mercy-pay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Créer un abonnement

> Inscrit un client à un plan. L'abonnement reste `incomplete` jusqu'au premier paiement : envoyez à votre client la page `next_action.redirect_url`.

> [!TIP]
>
> Essayez avec une clé **sandbox** (`mp_sandbox_…`) : aucun argent réel ne bouge. Voir [Test et production](https://docs.mercy-pay.com/start/environments.md).

Envoyez la page `next_action.redirect_url` à votre client. Il y paie le premier cycle par carte ou mobile money, ou enregistre son moyen de paiement si le plan a un essai encore disponible pour lui. L'abonnement devient `active` (ou `trialing`) et vous recevez `subscription.created`. Sans paiement sous 48 heures, l'inscription est annulée.

| Erreur | Cause |
| - | - |
| `422` sur `customer_email` | Adresse absente : elle est obligatoire |
| `422` sur `customer_provider` | Opérateur invalide pour `customer_country`, ou numéro invalide |
| `422` sur `plan` | Plan introuvable ou archivé |
| `422` sur `subscription` | Le client a déjà un abonnement en cours à ce plan |

Voir [Paiement récurrent pas à pas](https://docs.mercy-pay.com/subscriptions/api-guide.md).


## OpenAPI

````yaml POST /api/v1/subscriptions
openapi: 3.1.0
info:
  title: API Mercy Pay
  version: 1.0.0
  description: >-
    Encaissez par mobile money et par carte, facturez en récurrent et versez vos
    fonds. Les montants sont des entiers en unités mineures. Chaque réponse
    porte un en-tête `Request-Id`.
servers:
  - url: https://mercy-pay.com
    description: >-
      Production. La clé choisit l'environnement : `mp_sandbox_…` ou
      `mp_live_…`.
security:
  - bearerAuth: []
tags:
  - name: Compte
  - name: Sessions de paiement
  - name: Paiements
  - name: Remboursements
    description: Rendre un paiement au client, en entier.
  - name: Liens de paiement
  - name: Plans
  - name: Abonnements
  - name: Clients
  - name: Factures
  - name: Espace client
  - name: Soldes
  - name: Retraits
  - name: État des opérateurs
  - name: Événements
    description: Tout ce qui s'est passé sur le compte, envoyé ou non à un endpoint.
  - name: Endpoints de webhook
    description: Les URL de votre serveur qui reçoivent les événements.
paths:
  /api/v1/subscriptions:
    post:
      tags:
        - Abonnements
      summary: Créer un abonnement
      description: >-
        Inscrit un client à un plan. L'abonnement reste `incomplete` jusqu'au
        premier paiement : envoyez à votre client la page
        `next_action.redirect_url`.
      operationId: createSubscription
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - plan
                - customer_name
                - customer_email
              properties:
                customer_name:
                  type: string
                  description: Nom du client
                  maxLength: 255
                customer_email:
                  type: string
                  description: Reçoit les reçus, les rappels et l'accès à l'espace client
                  format: email
                  maxLength: 255
                payment_method:
                  type: string
                  description: Sans ce champ, le client choisit sur la page de paiement
                  enum:
                    - card
                    - mobile_money
                customer_phone:
                  type: string
                  description: >-
                    Numéro mobile money. Avec `payment_method: "mobile_money"`,
                    la première demande part tout de suite sur ce téléphone.
                  maxLength: 32
                customer_country:
                  type: string
                  description: >-
                    Avec `customer_phone`. Pays ISO 3166-1 alpha-3, par exemple
                    `CMR`.
                customer_provider:
                  type: string
                  description: >-
                    Avec `customer_phone`. Code opérateur, par exemple
                    `MTN_MOMO_CMR`. Voir [Pays et
                    opérateurs](/payments/overview#pays-et-operateurs).
                  maxLength: 64
                max_cycles:
                  type: integer
                  description: Nombre d'échéances avant l'arrêt, de 1 à 1000
                  minimum: 1
                  maximum: 1000
                metadata:
                  type:
                    - object
                    - 'null'
                  additionalProperties:
                    type:
                      - string
                      - 'null'
                    maxLength: 500
                  description: >-
                    Vos données libres, renvoyées telles quelles. 50 clés
                    maximum, valeurs de 500 caractères maximum.
                plan:
                  type: string
                  description: Identifiant d'un de vos plans actifs (`plan_…`)
            example:
              plan: plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48
              payment_method: mobile_money
              customer_name: Awa Ndiaye
              customer_email: awa@example.com
              customer_phone: '670000012'
              customer_country: CMR
              customer_provider: MTN_MOMO_CMR
              metadata:
                customer_id: cus_1042
      responses:
        '201':
          description: Abonnement créé, `incomplete` jusqu'au premier paiement
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Subscription'
              example:
                data:
                  id: sub_4f2c8a1e0b7d4c559a3e6d1f8b2c7e90
                  object: subscription
                  livemode: false
                  created: '2026-11-07T10:00:00+00:00'
                  plan:
                    id: plan_9b1e4c2a6d3f4a5e8f6c2d9b1a7e3c48
                    object: plan
                    livemode: false
                    created: '2026-11-01T09:00:00+00:00'
                    name: Abonnement Pro
                    description: null
                    amount: 5000
                    currency: XAF
                    interval: monthly
                    interval_count: 1
                    trial_period_days: 0
                    status: active
                    portal_switchable: true
                    metadata: null
                  pending_plan: null
                  payment_method: mobile_money
                  customer: cus_c71d0e5a3b2f4e8a9c1d5f6a7b8c9d01
                  customer_name: Awa Ndiaye
                  customer_email: awa@example.com
                  customer_phone: '237670000012'
                  customer_country: CMR
                  customer_provider: MTN_MOMO_CMR
                  status: incomplete
                  current_period_start: null
                  current_period_end: null
                  trial_ends_at: null
                  next_billing_at: null
                  cancel_at_period_end: false
                  canceled_at: null
                  paused_at: null
                  resumes_at: null
                  max_cycles: null
                  cycles_billed: 0
                  metadata:
                    customer_id: cus_1042
                  next_action:
                    type: redirect
                    redirect_url: >-
                      https://mercy-pay.com/abonnements/4f2c8a1e-0b7d-4c55-9a3e-6d1f8b2c7e90/payer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 255
      description: >-
        Une clé unique par opération : la rejouer pendant 24 heures renvoie la
        même réponse sans rien créer de plus. Voir
        [Idempotence](/developers/idempotency).
  schemas:
    Subscription:
      type: object
      properties:
        id:
          type: string
          description: Identifiant de l'abonnement (`sub_…`)
        object:
          type: string
          const: subscription
        livemode:
          type: boolean
          description: '`true` pour de l''argent réel (clé live), `false` en sandbox'
        created:
          type:
            - string
            - 'null'
          format: date-time
          description: Date de création, ISO 8601
        plan:
          $ref: '#/components/schemas/Plan'
        pending_plan:
          type:
            - string
            - 'null'
          description: Plan appliqué à la prochaine échéance (`plan_…`), `null` sinon
        payment_method:
          type: string
          description: Moyen choisi à l'inscription
          enum:
            - card
            - mobile_money
        customer:
          type:
            - string
            - 'null'
          description: Client (`cus_…`). Avec `expand[]=customer`, le client complet
        customer_name:
          type: string
          description: Nom du client
        customer_email:
          type: string
          description: 'E-mail du client : reçus, rappels et accès à l''espace client'
        customer_phone:
          type:
            - string
            - 'null'
          description: Numéro mobile money
        customer_country:
          type:
            - string
            - 'null'
          description: Pays du numéro
        customer_provider:
          type:
            - string
            - 'null'
          description: Opérateur du numéro
        status:
          type: string
          description: Voir [le cycle de vie](/subscriptions/overview#cycle-de-vie).
          enum:
            - incomplete
            - trialing
            - active
            - past_due
            - unpaid
            - paused
            - canceled
        current_period_start:
          type:
            - string
            - 'null'
          format: date-time
          description: Début de la période en cours
        current_period_end:
          type:
            - string
            - 'null'
          format: date-time
          description: Fin de la période en cours
        trial_ends_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Fin de l'essai
        next_billing_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Prochaine échéance
        cancel_at_period_end:
          type: boolean
          description: '`true` : s''arrête à la fin de la période payée'
        canceled_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Date d'annulation
        paused_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Date de mise en pause
        resumes_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Reprise automatique prévue
        max_cycles:
          type:
            - integer
            - 'null'
          description: >-
            Nombre d'échéances après lequel l'abonnement s'arrête, `null` sans
            limite.
        cycles_billed:
          type: integer
          description: Échéances déjà facturées
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: Vos données libres. Voir [Metadata](/developers/metadata).
        next_action:
          $ref: '#/components/schemas/NextAction'
    Plan:
      type: object
      properties:
        id:
          type: string
          description: Identifiant du plan (`plan_…`)
        object:
          type: string
          const: plan
        livemode:
          type: boolean
          description: '`true` pour de l''argent réel (clé live), `false` en sandbox'
        created:
          type:
            - string
            - 'null'
          format: date-time
          description: Date de création, ISO 8601
        name:
          type: string
          description: Nom affiché au client
        description:
          type:
            - string
            - 'null'
          description: Description
        amount:
          type: integer
          description: Prix d'un cycle, en unités mineures
        currency:
          type: string
          description: Devise
        interval:
          type: string
          description: Rythme
          enum:
            - daily
            - weekly
            - monthly
            - yearly
        interval_count:
          type: integer
          description: 'Nombre d''`interval` par cycle : `monthly` et `3`, tous les 3 mois.'
        trial_period_days:
          type: integer
          description: Jours d'essai, `0` sans essai
        status:
          type: string
          description: '`archived` : plus de nouvel abonné'
          enum:
            - active
            - archived
        portal_switchable:
          type: boolean
          description: Vos abonnés peuvent passer à ce plan depuis leur espace client.
        active_subscriptions_count:
          type: integer
          description: >-
            Abonnements en essai, actifs, en retard, impayés ou en pause.
            Renvoyé par la liste seulement.
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: Vos données libres. Voir [Metadata](/developers/metadata).
    NextAction:
      type:
        - object
        - 'null'
      description: >-
        Ce que votre client doit encore faire. Décidez selon `type`, pas selon
        la seule présence du champ : un futur moyen de paiement pourra ajouter
        son propre `type`.
      properties:
        type:
          type: string
          description: >-
            `card_payment` (confirmer une carte depuis votre formulaire) ou
            `redirect` (envoyer le client sur `redirect_url`).
          enum:
            - card_payment
            - redirect
        client_secret:
          type: string
          description: >-
            `card_payment` : secret qui confirme le paiement depuis votre
            formulaire.
        redirect_url:
          type: string
          description: '`redirect` : page hébergée où le client termine.'
          format: uri
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
                - authentication_error
                - invalid_request_error
                - idempotency_error
                - payment_error
                - rate_limit_error
                - api_error
            code:
              type: string
              description: Code stable, à tester dans votre code
            message:
              type: string
              description: Explication en français, pour un humain
            param:
              type:
                - string
                - 'null'
              description: Le premier champ en cause
            doc_url:
              type: string
              description: Explication de ce code
            request_id:
              type: string
              description: >-
                Identifiant de la requête (`req_…`), aussi dans l'en-tête
                `Request-Id`
            errors:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
              description: Détail par champ refusé, sur une erreur `422`
      required:
        - error
  responses:
    Unauthorized:
      description: Clé absente, invalide ou révoquée
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: authentication_error
              code: api_key_invalid
              message: Clé API invalide.
              param: null
              doc_url: https://docs.mercy-pay.com/developers/errors#api_key_invalid
              request_id: req_01jc8x4m2v7t9qk3n5w6y8z0ab
    Forbidden:
      description: Compte suspendu, ou clé live avant l'approbation du KYC
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: authentication_error
              code: live_mode_requires_kyc
              message: Le mode live demande un KYC approuvé.
              param: null
              doc_url: >-
                https://docs.mercy-pay.com/developers/errors#live_mode_requires_kyc
              request_id: req_01jc8x4m2v7t9qk3n5w6y8z0ab
    Conflict:
      description: Conflit avec l'état de la ressource, ou clé d'idempotence réutilisée
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: invalid_request_error
              code: conflict
              message: 'Cette ressource est utilisée : archivez-la.'
              param: null
              doc_url: https://docs.mercy-pay.com/developers/errors#conflict
              request_id: req_01jc8x4m2v7t9qk3n5w6y8z0ab
    ValidationFailed:
      description: Données invalides, avec le détail dans `errors`
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: invalid_request_error
              code: validation_failed
              message: Le champ amount est obligatoire.
              param: amount
              doc_url: https://docs.mercy-pay.com/developers/errors#validation_failed
              request_id: req_01jc8x4m2v7t9qk3n5w6y8z0ab
              errors:
                amount:
                  - Le champ amount est obligatoire.
    RateLimited:
      description: Plus de 60 requêtes par minute avec cette clé
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: rate_limit_error
              code: rate_limited
              message: Trop de requêtes.
              param: null
              doc_url: https://docs.mercy-pay.com/developers/errors#rate_limited
              request_id: req_01jc8x4m2v7t9qk3n5w6y8z0ab
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Votre clé API. Utilisez une clé sandbox (`mp_sandbox_…`) pour essayer :
        aucun argent réel ne bouge.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.