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

# Reconciling subscription features

> Converge an active subscription's feature entitlements to its plan after the plan's features change

When you add or remove features on a plan, existing active subscriptions do not automatically pick up the change — their entitlements stay as they were at activation. Reconciliation converges an active subscription's feature entitlements to its plan's current shape in a single call: it provisions missing features, removes features no longer on the plan, and synchronizes the corresponding billing.

<Info>
  Reconciliation applies to Standard Billing (`billingVersion: 1`) subscriptions billed on their plan's line-item schedule. Legacy Billing subscriptions are rejected. See [Billing Versions](/platform/billing/billing-versions).
</Info>

## When to use it

Reach for reconciliation whenever a plan's feature list drifts from what an existing active subscription actually has:

* You **added a feature** to a plan and want existing subscribers to get it without recreating the subscription.
* You **removed a feature** from a plan and want to stop billing existing subscribers for it and revoke their entitlement.
* A prior subscription activation partially failed and left the subscription without one of the plan's features.
* You want to **preview** either of the above without touching billing — use `dryRun: true`.

You do not need reconciliation when the plan and subscription are already aligned. New subscriptions provision entitlements automatically at activation; see [Automatic entitlement provisioning](/platform/pricing/features#automatic-entitlement-provisioning).

## What reconciliation does

A reconciliation is a single, idempotent operation that:

1. **Adds** an entitlement (and, for metered features, its initial grant) for every plan feature the subscription is missing.
2. **Removes** the entitlement and voids the grants of any feature the subscription has but the plan no longer includes.
3. **Skips** features that are already present — they are left unchanged.
4. **Synchronizes billing** by creating future line items for added prices and removing future, uninvoiced line items for removed prices.

Reconciliation mints metered feature grants as full-period grants and does not prorate for the current period.

Reconciliation never modifies already-invoiced line items — it only touches future, uninvoiced periods.

## Making a reconciliation request

<Info>
  **API Reference**: See the [Reconcile Features API endpoint](/api-reference/subscriptions/reconcile-features) for the complete request and response schema.
</Info>

Send an empty body (or `{ "dryRun": false }`) to a subscription's reconciliation endpoint. The subscription must be active.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function reconcileSubscription(subscriptionId, { dryRun = false } = {}) {
    const response = await fetch(
      `https://api.paygentic.io/v0/subscriptions/${subscriptionId}/reconciliations`,
      {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer sk_live_YOUR_API_KEY',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ dryRun }),
      }
    );

    if (!response.ok) {
      throw new Error(`Reconciliation failed: ${response.status}`);
    }

    return response.json();
  }
  ```

  ```python Python theme={null}
  import requests

  def reconcile_subscription(subscription_id, dry_run=False):
      url = f"https://api.paygentic.io/v0/subscriptions/{subscription_id}/reconciliations"
      headers = {
          "Authorization": "Bearer sk_live_YOUR_API_KEY",
          "Content-Type": "application/json",
      }
      response = requests.post(url, json={"dryRun": dry_run}, headers=headers)
      response.raise_for_status()
      return response.json()
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.paygentic.io/v0/subscriptions/{subscriptionId}/reconciliations \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"dryRun": false}'
  ```
</CodeGroup>

### Previewing with dry run

Pass `dryRun: true` to compute the same diff without creating any entitlement, grant, or line item. The response is shaped identically to a real run and reports exactly what a subsequent real call would do. Use it to confirm the scope of a change before applying it.

```json theme={null}
{
  "dryRun": true
}
```

## Response

The response returns a `subscriptionReconciliation` object describing the outcome:

```json theme={null}
{
  "object": "subscriptionReconciliation",
  "dryRun": false,
  "features": {
    "added": [
      {
        "featureId": "feat_a1b2c3d4e5f6g7h8",
        "featureKey": "advanced_reporting",
        "billed": true
      }
    ],
    "skipped": [
      {
        "featureId": "feat_i9j0k1l2m3n4o5p6",
        "featureKey": "basic_dashboard"
      }
    ],
    "removed": [
      {
        "featureId": "feat_q7r8s9t0u1v2w3x4",
        "featureKey": "legacy_export"
      }
    ],
    "failed": []
  },
  "lineItems": {
    "created": 1,
    "removed": 1
  }
}
```

### Feature groups

* **`added`** — Features the subscription was missing that were provisioned. A `billed: false` entry means the feature's price does not produce a recurring line item, so the feature is entitled but not billed by this reconciliation. Examples include an instant price, a credit-denominated price, or an already-elapsed one-time charge.
* **`skipped`** — Features the subscription already had an active entitlement for. Left unchanged.
* **`removed`** — Features no longer on the plan. Their entitlement was canceled and grants voided so the subscription matches the plan's current features.
* **`failed`** — Features that could not be fully provisioned. Never billed by this call. Each entry has a coded `reason`:
  * `entitlement_failed` — the entitlement itself could not be created.
  * `grant_mint_failed` — the entitlement was created but its initial metered grant could not be minted. Re-running reconciliation retries the mint safely.

### Line items

* **`created`** — Number of features newly billed on the subscription by this reconciliation. A dry-run preview returns the same count as the applied run.
* **`removed`** — Number of features whose billing was removed from the subscription's upcoming (not-yet-invoiced) invoice because their price is no longer on the plan.
* **`syncFailed`** — Present and `true` when entitlements were provisioned successfully but the line-item synchronization step failed. Safe to retry.

## Status codes

| Status            | Meaning                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `201 Created`     | Reconciliation applied. Feature entitlements match the plan and billing was fully synchronized.                                                                                                                                                                                                                                                                           |
| `200 OK`          | The reconciliation did not create a full set of resources. This covers a `dryRun` preview, a run where one or more features could not be provisioned (billing is withheld until they succeed), or a run whose entitlements provisioned but whose line-item synchronization failed. Safe to retry — the API does not duplicate already-provisioned entitlements or grants. |
| `400 Bad Request` | Request is malformed, or the subscription is not eligible. See error codes below.                                                                                                                                                                                                                                                                                         |
| `404 Not Found`   | Subscription does not exist or is not visible to your merchant.                                                                                                                                                                                                                                                                                                           |

### Error codes

* `SUBSCRIPTION_NOT_ACTIVE` — The subscription is not in the `active` state. Reconciliation is only available for active subscriptions.
* `SUBSCRIPTION_NOT_V1_BILLING` — The subscription is on Legacy Billing (`billingVersion: 0`). Reconciliation only applies to Standard Billing subscriptions.

## Idempotency and retries

Reconciliation is safe to retry:

* The API detects already-provisioned entitlements and grants and does not duplicate them.
* If a previous run created an entitlement but failed to mint its initial metered grant, re-running reconciliation retries the mint on the same idempotency key. The mint is a no-op when the grant is already present.
* If a run returns `200` with `lineItems.syncFailed: true`, retry to complete billing synchronization.

## Rate limits

Reconciliations are limited to **2 requests per second per subscription**. Automated retries should include backoff; a `429 Too Many Requests` response returns the standard rate-limit error message.

## Related pages

* [Features](/platform/pricing/features) — Configure features and link them to plan prices
* [Editing prices](/platform/pricing/editing-prices) — How other plan edits propagate to existing subscribers
* [Billing Versions](/platform/billing/billing-versions) — Standard vs. Legacy Billing
* [Customer Lifecycle](/platform/billing/customer-lifecycle) — End-to-end subscription flows
