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

# Create a Subscription

> Enroll a customer's card against a plan and start automatic recurring billing.

## Overview

A **Subscription** links a customer's card to a plan and starts the recurring billing cycle. Once created, Payaza automatically charges the card on every billing date defined by the plan — you do not need to trigger each charge manually.

<Warning>
  **A plan must exist before you can create a subscription.** If you have not created a plan yet, go to [Subscription Plans](/guides/subscription-plans) first, create a plan, and copy the `planCode` from the response. You will need it here.
</Warning>

***

## Key Requirements

<Info>
  Retrieve your API keys from your dashboard by following the steps on our [Authentication](/guides/authentication) page. Use your **public API key** for all subscription requests.
</Info>

Set the following headers on every request:

```json theme={null}
{
  "Authorization": "Payaza <Public API Key encoded in base 64>"
}
```

***

## How subscriptions work

When you call Create Subscription, three things happen in one request:

1. **Card enrolment** — the customer's card is validated and stored for recurring charges.
2. **First charge** — if no trial period is set, the first payment is attempted immediately.
3. **Billing schedule** — the next charge date is calculated and set based on the plan's interval.

After that, Payaza handles all future charges automatically on each renewal date.

<AccordionGroup>
  <Accordion title="No trial period — immediate charge">
    The subscription is created and the first charge is attempted right away. If the card is charged successfully, the subscription status becomes `ACTIVE` and `nextChargeDate` is set to one billing cycle from now.

    If the charge fails, the subscription enters `INCOMPLETE` status until a successful payment is received.
  </Accordion>

  <Accordion title="With trial period — deferred charge">
    The card is enrolled immediately but no charge is attempted. The subscription status becomes `TRIALING`. The first charge is deferred until the end of the trial period. Until then, the customer has free access.
  </Accordion>

  <Accordion title="3DS authentication required">
    Some cards require the customer to complete a 3D Secure challenge before the first charge can go through. In this case, the subscription is created with a status of `REQUIRES_ACTION` and the response includes `threeDsUrl`, `formData`, and `threeDsHtml` — handle these exactly as described in the [Card Collections guide](/guides/card-collections).

    Once the customer completes the 3DS challenge, the subscription moves to `ACTIVE`.
  </Accordion>
</AccordionGroup>

***

## Subscription statuses

Understanding what each status means before you build will save you debugging time later.

| Status            | What it means                                                                                |
| ----------------- | -------------------------------------------------------------------------------------------- |
| `ACTIVE`          | Subscription is live and billing is running normally                                         |
| `TRIALING`        | Trial period is active — card enrolled but not yet charged                                   |
| `INCOMPLETE`      | Created but first charge has not yet succeeded                                               |
| `REQUIRES_ACTION` | 3DS authentication is required from the customer                                             |
| `PAUSED`          | Billing temporarily suspended — see [Subscription Lifecycle](/guides/subscription-lifecycle) |
| `CANCELLED`       | Permanently cancelled — no further charges will occur                                        |
| `PAST_DUE`        | A recurring charge failed and the subscription is pending retry                              |

***

## Create a Subscription

<Note>
  `cardDetails` is mandatory. You cannot create a subscription without a card — Payaza uses it to enroll the customer for all future recurring charges.
</Note>

<CodeGroup>
  ```bash cURL Request theme={null}
  curl --request POST \
    --url https://api.payaza.africa/live/subscription/api/v1/subscriptions \
    --header 'Authorization: Payaza <Public API Key encoded in base 64> \
    --header 'Content-Type: application/json' \
    --data '{
      "customerEmail": "johndoe@example.com",
      "planCode": "PLN_821DCFD",
      "cardDetails": {
        "cardNumber": "4187451844054629",
        "expiryMonth": "07",
        "expiryYear": "27",
        "securityCode": "100",
        "cardHolderName": "John Doe",
        "present": true
      }
    }'
  ```
</CodeGroup>

**Request body parameters**

| Parameter                    | Type    | Required | Description                                                                            |
| ---------------------------- | ------- | -------- | -------------------------------------------------------------------------------------- |
| `customerEmail`              | String  | ✓        | The customer's email address. One email can only have one active subscription per plan |
| `planCode`                   | String  | ✓        | The `planCode` from the Create Plan response — links this subscription to a plan       |
| `cardDetails`                | Object  | ✓        | The card details used to enrol the customer for recurring billing                      |
| `cardDetails.cardNumber`     | String  | ✓        | The customer's card number                                                             |
| `cardDetails.expiryMonth`    | String  | ✓        | Two-digit expiry month (e.g. `"07"`)                                                   |
| `cardDetails.expiryYear`     | String  | ✓        | Two-digit expiry year (e.g. `"27"`)                                                    |
| `cardDetails.securityCode`   | String  | ✓        | CVV / security code                                                                    |
| `cardDetails.cardHolderName` | String  | ✓        | Name printed on the card                                                               |
| `cardDetails.present`        | Boolean | ✓        | Always set to `true` to validate the card details                                      |

***

### Response types

Check `data.subscription.status` to understand what happened, then check `data.chargeResult` for the payment outcome.

**Response 1 — Subscription active, first charge successful**

```json theme={null}
{
  "success": true,
  "message": "Subscription created",
  "data": {
    "subscription": {
      "id": 20,
      "customerEmail": "johndoe@example.com",
      "status": "ACTIVE",
      "currentPeriodStart": "2026-07-08T19:35:52.348327",
      "currentPeriodEnd": "2026-08-08T19:35:55.569925",
      "nextChargeDate": "2026-08-08T19:35:55.569925",
      "trialEndDate": null,
      "enrollmentReference": "SUBREF_BFE417ABF6FF49A985",
      "enrollmentTxnReference": "SUB_ENROLL_20_1783539352354",
      "chargeMode": "DIRECT_CHARGE",
      "enrollConfirmed": true
    },
    "chargeResult": {
      "charged": true,
      "status": "SUCCESS",
      "amount": 5000,
      "fees": 0,
      "currency": "NGN",
      "transactionReference": "SUB_ENROLL_20_1783539352354",
      "authCode": "581032",
      "chargeMode": "DIRECT_CHARGE",
      "message": "Approved"
    }
  }
}
```

**Response 2 — Trial period active, no charge yet**

```json theme={null}
{
  "success": true,
  "message": "Subscription created",
  "data": {
    "subscription": {
      "id": 944,
      "customerId": null,
      "customerEmail": "johndoe@gmail.com",
      "firstName": null,
      "lastName": null,
      "customerAuthorizedAt": null,
      "status": "TRIALING",
      "currentPeriodStart": "2026-08-16T15:37:22.754748",
      "currentPeriodEnd": "2026-09-15T15:37:22.754748",
      "nextChargeDate": "2026-09-15T15:37:22.754748",
      "trialEndDate": "2026-09-15T15:37:22.754748",
      "pauseResumesAt": null,
      "cancelAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "cancellationReason": null,
      "cardExpiryAlertSent": false,
      "connectionMode": "Live",
      "enrollmentReference": "SUBREF_805B6F945FF342FF92",
      "enrollmentTxnReference": "SUB_ENROLL_944_1786894642764",
      "chargeMode": "DIRECT_CHARGE",
      "metadata": null,
      "createdAt": "2026-08-16T15:37:22.754832",
      "updatedAt": "2026-08-16T15:37:22.881736",
      "customerBearsFee": false,
      "enrollConfirmed": true
    },
    "chargeResult": {
      "charged": false,
      "status": "SUCCESS",
      "fees": 0,
      "currency": "NGN",
      "transactionReference": "SUB_ENROLL_944_1786894642764",
      "authCode": "",
      "chargeMode": "DIRECT_CHARGE",
      "message": "Your card has been saved successfully."
    }
  }
}
```

**Response 3 — 3DS authentication required**

```json theme={null}
{
  "success": true,
  "message": "Subscription created",
  "data": {
    "subscription": {
      "id": 22,
      "customerEmail": "johndoe@example.com",
      "status": "REQUIRES_ACTION",
      "enrollConfirmed": false
    },
    "chargeResult": {
      "charged": false,
      "do3dsAuth": true,
      "threeDsUrl": "https://secure-acs.example.com/creq/...",
      "formData": "eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6...",
      "threeDsHtml": "<div id='threedsChallengeRedirect'>...</div>"
    }
  }
}
```

<Warning>
  When `status: "REQUIRES_ACTION"`, the subscription is created but the card has **not** been confirmed yet. You must complete the 3DS flow before Payaza can enrol the card and begin billing. See the [Card Collections guide](/guides/card-collections) for the full 3DS HTML integration.
</Warning>

***

## Key fields to save from the response

| Field                               | Where                                    | Why you need it                                                                   |
| ----------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------- |
| `subscription.id`                   | `data.subscription.id`                   | Required for all lifecycle operations — pause, cancel, change plan, and all reads |
| `subscription.nextChargeDate`       | `data.subscription.nextChargeDate`       | Show the customer when they will be charged next                                  |
| `subscription.enrollmentReference`  | `data.subscription.enrollmentReference`  | Tracks the card enrolment for this subscription                                   |
| `chargeResult.transactionReference` | `data.chargeResult.transactionReference` | Reference for the initial enrollment charge                                       |

***

## Error handling

| Scenario                  | What to check                                                                                                                  |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `409 Conflict`            | The `customerEmail` already has an active subscription on this plan. Each email can only have one active subscription per plan |
| `status: INCOMPLETE`      | The first charge failed. Check `chargeResult.message` for the decline reason — the customer's card may need updating           |
| `status: REQUIRES_ACTION` | 3DS is required — render the `threeDsHtml` in your page and complete the authentication challenge                              |
| `success: false`          | Check the `message` field. Common causes: invalid `planCode`, missing required fields, or malformed card details               |

***

## Developer notes

* Save `data.subscription.id` from every Create Subscription response — this integer `id` is what all lifecycle and read operations use (not `planCode`, not `enrollmentReference`).
* One `customerEmail` can only have one active subscription per plan. If a customer tries to subscribe to the same plan twice, the API returns a `409` conflict. Cancel the existing subscription first if you need to re-subscribe them.
* `present: true` in `cardDetails` is always required — it tells the system to validate the card data as part of enrolment.
* When `chargeMode` is `DIRECT_CHARGE`, Payaza charges the card directly on each billing cycle. The card service determines the charge mode automatically during enrolment — you do not set it.
* For NGN cards, `securityCode` is the card's CVV. For some non-NGN cards, a PIN may be required — refer to the [Card Collections guide](/guides/card-collections) for PIN handling.

***

## What's next

Now that you have an active subscription, explore how to manage it over time:

<CardGroup cols={2}>
  <Card title="Subscription Lifecycle" icon="arrows-spin" href="/guides/subscription-lifecycle">
    Pause, resume, cancel, or move a customer to a different plan.
  </Card>

  <Card title="Subscription Reads" icon="chart-bar" href="/guides/subscription-reads">
    Fetch subscription details, charge history, upcoming charges, and event logs.
  </Card>
</CardGroup>
