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

# Catalog

> Manage plans, feature costs, and one-time credit packs as individual resources.

## Catalog model

Ledger has three item types in each environment's catalog. Your API key selects the environment; Sandbox and Live items are separate:

* **Features** define the credit cost of an application action. A feature ID such as `api_call` is passed to check and track.
* **Plans** grant a positive number of credits each month. A price of `0` defines a free plan that activates without Checkout; paid plans sync to the Stripe account connected to that environment.
* **Add-ons** are one-time `credit_pack` purchases that grant a fixed number of credits after payment.

IDs use letters, numbers, `_`, and `-`, with a maximum length of 100 characters. Names are 1–120 characters. Credit amounts are positive integers. Prices are integer cents; add-on prices must be positive.

## Create items independently

The dashboard has a form and list for each item under **Catalog → Plans**, **Features**, and **Add-ons**. The API creates each one with `POST /v1/features`, `POST /v1/plans`, or `POST /v1/add-ons`. Each response contains an `item` and `changed: true`. A duplicate active ID returns `409 already_exists`.

```bash theme={null}
curl --request POST https://api.integrateledger.com/v1/features \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"id":"api_call","name":"API call","creditCost":1}'

curl --request POST https://api.integrateledger.com/v1/plans \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"id":"starter","name":"Starter","monthlyCredits":100,"monthlyPriceCents":1900}'

curl --request POST https://api.integrateledger.com/v1/add-ons \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"id":"extra_credits","name":"Extra credits","type":"credit_pack","creditAmount":50,"priceCents":500}'
```

For each resource, `GET /v1/{resource}` lists active items, `GET /v1/{resource}/{id}` reads one, `PUT /v1/{resource}/{id}` updates one, and `DELETE /v1/{resource}/{id}` archives one. The [API reference](https://docs.integrateledger.com/api-reference/plans/create.md) includes cURL and labeled [Python](https://docs.integrateledger.com/python-sdk.md) and [TypeScript](https://docs.integrateledger.com/sdk.md) SDK source examples for every operation.

## Update a plan

`PUT /v1/plans/{id}` requires the complete editable plan body. When price, allowance, or name changes, Ledger archives the current row and inserts a new version. Existing subscriptions keep their referenced version, while new Checkouts use the current version. An unchanged update returns `changed: false`. Paid versions report `stripeSync` as `synced`, `pending`, `failed`, or `awaiting_connection`.

```bash theme={null}
curl --request PUT https://api.integrateledger.com/v1/plans/starter \
  -H "Authorization: Bearer $LEDGER_API_KEY" -H "Content-Type: application/json" \
  --data '{"name":"Starter","monthlyCredits":200,"monthlyPriceCents":2900}'
```

Feature and add-on updates also use complete editable bodies but update their existing rows. Archiving hides an item from active lists and new purchases. Historical usage, subscriptions, and purchases remain visible.

## Recover a failed Stripe plan sync

Paid plans must show **Synced** in **Catalog → Plans** before customers can buy them. A **Failed** plan displays a short reason; an **Awaiting Stripe** plan has not finished syncing, including after an interrupted sync. Both show **Retry sync**. Connect Stripe in the selected environment or fix a rejected key or permission, then retry from the plan row. A retry reuses the same plan version and Stripe idempotency keys, so it does not create a new plan or charge a customer. Stripe can retain a failed idempotent response; if a retry still fails, try later or contact support. If the Product or Price no longer matches the plan, contact support before retrying.

The retry action belongs to the signed-in dashboard session for the selected environment; it is not a public SDK endpoint. Add-ons create their Stripe Checkout resources when a customer buys them, so they do not have a catalog sync status.

## Whole-catalog publishing

`PUT /v1/catalog` remains available for declarative sync from a config file. It replaces the desired active catalog: items omitted from its lists are archived. Use item APIs for dashboard onboarding and incremental setup so one creation does not affect unrelated items. The whole-catalog input requires at least one plan and one feature.


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