Catalog

The Catalog is at the heart of the Kill Bill subscription and billing systems. It provides complete current information on products available, subscription plans, billing options, and much more. Each tenant has a single catalog, but different tenants may have completely different catalogs.

The catalog for a given tenant may be updated to new versions from time to time. This provides the ability to deprecate old products, add new ones, or change prices for existing products. Older versions remain available in case they are needed. If a new version is uploaded, new subscriptions will use the new version, but existing subscriptions may either depend on their original versions (grandfathering use case) or use the latest version (price update use case). Please refer to the catalog section of our subscription billing documentation for additional details.

KAUI, our admin UI, provides the ability to upload a simple plan. The simple plan provides a way to ease testing and to play with the system. See API section Simple Plan.

A tenant has several options for setting up their catalog. You can choose the option that best meets your needs:

  1. Use the default test catalog that ships with Kill Bill by creating a tenant with useGlobalDefault=true
  2. Use the Simple Plan API from KAUI to get started quickly (no need to create an XML catalog, simply use the UI and add the plans you need).
  3. Write your own complete catalog as an XML file and upload it. Some examples of catalog can be found in our test repo. For validation, check our manual or use our cloud validation tool after creating an account.
  4. Write a custom Catalog plugin. This is only for advanced users with special needs.

For a full discussion of the KillBill catalog, see the Catalog section in the Subscription Guide.

The Catalog API offers basic CRUD operations, allowing you to upload, retrieve and delete catalog versions.

Catalog Resource

At a very high level, the Catalog consists of the following main components:

Full details about the elements of a Catalog are given in the links cited above. The Catalog is maintained as an XML file.

CatalogValidation Resource

Represents the result of validating a catalog.

It includes the following attribute:

Name Type Generated by Description
catalogValidationErrors list system List of CatalogValidationError (see next)

CatalogValidationError:

Represents a catalog validation error.

It includes the following attributes:

Name Type Generated by Description
errorDescription string system Description about the error

Catalog

Upload a catalog as XML

This endpoint uploads a complete catalog in XML format. This becomes the current version of the Catalog for this Tenant.

Note that the naming for the various entities is based on what the XML spec specifies, and in particular the name elements need to be globally unique in the catalog and must conform to the XML NCName definition. This means that they cannot contain symbol characters like :, @, $, %, &, /, +, ,, ;,, whitespace characters or parentheses, and they cannot begin with a number, dot or minus character.

HTTP Request

POST http://127.0.0.1:8080/1.0/kb/catalog/xml

Example Request:

curl -v \
    -X POST \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Content-Type: text/xml" \
    -H "Accept: application/json" \
    -H "X-Killbill-CreatedBy: demo" \
    -H "X-Killbill-Reason: demo" \
    -H "X-Killbill-Comment: demo" \
    -d '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><catalog> ...' \
    "http://127.0.0.1:8080/1.0/kb/catalog/xml"
import org.killbill.billing.client.api.gen.CatalogApi;
import java.nio.file.Files;
import java.nio.file.Path;

protected CatalogApi catalogApi;

Path filePath = Path.of("H:/killbill/spycarcatalog.xml");

String body = Files.readString(filePath);

catalogApi.uploadCatalogXml(body, requestOptions);
user = 'user'
reason = 'reason'
comment = 'comment'

catalog = KillBillClient::Model::Catalog

catalog_file_xml = File.read("H:/killbill/catalog.xml")

catalog.upload_tenant_catalog(catalog_file_xml,
                              user,
                              reason,
                              comment,
                              options)
catalogApi = killbill.CatalogApi()

xml_catalog = open("../resources/SpyCarBasic.xml", "r+").read()

catalogApi.upload_catalog_xml(xml_catalog, 
                              created_by='demo',
                              reason='reason', 
                              comment='comment')
const fs = require('fs');

const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const content = fs.readFileSync('H:/killbill/catalog.xml').toString();    

const response: AxiosResponse<string, any> = await catalogApi.uploadCatalogXml(content, 'created_by');
$xKillbillCreatedBy = "user";
$xKillbillReason = "reason";
$xKillbillComment = "comment";

$apiInstance = $client->getCatalogApi();

$filename = "H:/killbill/catalog.xml";

$fileContents = file_get_contents($filename);

$apiInstance->uploadCatalogXml($fileContents,$xKillbillCreatedBy,$xKillbillReason,$xKillbillComment);

Request Body

Contains the complete catalog in XML format

Query Parameters

None.

Response

If successful, returns a status code of 201 and an empty body.

Retrieve the catalog as XML

This endpoint retrieves the Catalog for a specified date in XML format. If there are multiple versions, the latest version with an effective date not later than the requested date is returned. If the effective date for all versions is greater than the requested date, the earliest version is returned.

For example, suppose there are two versions of the catalog, the current version dated 2020-01-01 (Jan. 1, 2020) and a previous version dated 2019-01-01. Then

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/xml

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: text/xml" \
    "http://localhost:8080/1.0/kb/catalog/xml"  
import org.killbill.billing.client.api.gen.CatalogApi;
import org.joda.time.DateTime;

protected CatalogApi catalogApi;

DateTime requestedDate = null;
UUID accountId = null;

String catalog = catalogApi.getCatalogXml(requestedDate, 
                                          accountId, 
                                          requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil

catalog_xml = catalog.get_tenant_catalog_xml(requested_date,options)
catalogApi = killbill.CatalogApi()

catalog_xml = catalogApi.get_catalog_xml()
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const catalogXml: AxiosResponse<string, any> = await catalogApi.getCatalogXml();
$apiInstance = $client->getCatalogApi();

$catalogXml = $apiInstance->getCatalogXml();

Example Response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <catalogs>
    <versions>
      <version>
        <effectiveDate>2013-02-08T00:00:00Z</effectiveDate>
        <catalogName>SpyCarBasic</catalogName>
        <currencies>
          <currency>USD</currency>
          <currency>GBP</currency>
        </currencies>
        <units/>
        <products>
          <product name="Basic" prettyName="Basic">
            <category>BASE</category>
            <included/>
            <available/>
            <limits/>
          </product>
          <product name="Sports" prettyName="Sports">
            <category>BASE</category>
            <included/>
            <available/>
            <limits/>
          </product>
          <product name="Standard" prettyName="Standard">
            <category>BASE</category>
            <included/>
            <available/>
            <limits/>
          </product>
          <product name="Super" prettyName="Super">
            <category>ADD_ON</category>
            <included/>
            <available/>
            <limits/>
          </product>
        </products>
        <rules>
          <changePolicy>
            <changePolicyCase>
              <policy>IMMEDIATE</policy>
            </changePolicyCase>
          </changePolicy>
          <changeAlignment>
            <changeAlignmentCase>
              <alignment>START_OF_BUNDLE</alignment>
            </changeAlignmentCase>
          </changeAlignment>
          <cancelPolicy>
            <cancelPolicyCase>
              <policy>IMMEDIATE</policy>
            </cancelPolicyCase>
          </cancelPolicy>
          <createAlignment>
            <createAlignmentCase>
              <alignment>START_OF_BUNDLE</alignment>
            </createAlignmentCase>
          </createAlignment>
          <billingAlignment>
            <billingAlignmentCase>
              <alignment>ACCOUNT</alignment>
            </billingAlignmentCase>
          </billingAlignment>
          <priceList>
            <priceListCase>
              <toPriceList>DEFAULT</toPriceList>
            </priceListCase>
          </priceList>
        </rules>
        <plans>
          <plan name="basic-annual" prettyName="basic-annual">
            <product>Basic</product>
            <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
            <initialPhases/>
            <finalPhase type="EVERGREEN">
              <duration>
                <unit>UNLIMITED</unit>
                <number>-1</number>
              </duration>
              <recurring>
                <billingPeriod>ANNUAL</billingPeriod>
                <recurringPrice>
                  <price>
                    <currency>USD</currency>
                    <value>1000</value>
                  </price>
                </recurringPrice>
              </recurring>
              <usages/>
            </finalPhase>
            <plansAllowedInBundle>-1</plansAllowedInBundle>
          </plan>
          <plan name="sports-monthly" prettyName="sports-monthly">
            <product>Sports</product>
            <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
            <initialPhases>
              <phase type="TRIAL">
                <duration>
                  <unit>DAYS</unit>
                  <number>30</number>
                </duration>
                <fixed type="ONE_TIME">
                  <fixedPrice/>
                </fixed>
                <usages/>
              </phase>
            </initialPhases>
            <finalPhase type="EVERGREEN">
              <duration>
                <unit>UNLIMITED</unit>
                <number>-1</number>
              </duration>
              <recurring>
                <billingPeriod>MONTHLY</billingPeriod>
                <recurringPrice>
                  <price>
                    <currency>GBP</currency>
                    <value>375.00</value>
                  </price>
                  <price>
                    <currency>USD</currency>
                    <value>500.00</value>
                  </price>
                </recurringPrice>
              </recurring>
              <usages/>
            </finalPhase>
            <plansAllowedInBundle>-1</plansAllowedInBundle>
          </plan>
          <plan name="standard-monthly" prettyName="standard-monthly">
            <product>Standard</product>
            <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
            <initialPhases>
              <phase type="TRIAL">
                <duration>
                  <unit>DAYS</unit>
                  <number>30</number>
                </duration>
                <fixed type="ONE_TIME">
                  <fixedPrice/>
                </fixed>
                <usages/>
              </phase>
            </initialPhases>
            <finalPhase type="EVERGREEN">
              <duration>
                <unit>UNLIMITED</unit>
                <number>-1</number>
              </duration>
              <recurring>
                <billingPeriod>MONTHLY</billingPeriod>
                <recurringPrice>
                  <price>
                    <currency>GBP</currency>
                    <value>75.00</value>
                  </price>
                  <price>
                    <currency>USD</currency>
                    <value>100.00</value>
                  </price>
                </recurringPrice>
              </recurring>
              <usages/>
            </finalPhase>
            <plansAllowedInBundle>-1</plansAllowedInBundle>
          </plan>
          <plan name="super-monthly" prettyName="super-monthly">
            <product>Super</product>
            <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
            <initialPhases>
              <phase type="TRIAL">
                <duration>
                  <unit>DAYS</unit>
                  <number>30</number>
                </duration>
                <fixed type="ONE_TIME">
                  <fixedPrice/>
                </fixed>
                <usages/>
              </phase>
            </initialPhases>
            <finalPhase type="EVERGREEN">
              <duration>
                <unit>UNLIMITED</unit>
                <number>-1</number>
              </duration>
              <recurring>
                <billingPeriod>MONTHLY</billingPeriod>
                <recurringPrice>
                  <price>
                    <currency>GBP</currency>
                    <value>750.00</value>
                  </price>
                  <price>
                    <currency>USD</currency>
                    <value>1000.00</value>
                  </price>
                </recurringPrice>
              </recurring>
              <usages/>
            </finalPhase>
            <plansAllowedInBundle>-1</plansAllowedInBundle>
          </plan>
        </plans>
        <priceLists>
          <defaultPriceList name="DEFAULT">
            <plans>
              <plan>basic-annual</plan>
              <plan>sports-monthly</plan>
              <plan>standard-monthly</plan>
              <plan>super-monthly</plan>
            </plans>
          </defaultPriceList>
        </priceLists>
      </version>
    </versions>
    <catalogName>SpyCarBasic</catalogName>
  </catalogs>

Query Parameters

Name Type Required Default Description
requestedDate string false current date requested date

Response

If successful, returns a status code of 200 and the catalog for the requested date in XML format.

Retrieve the catalog as JSON

This endpoint retrieves the Catalog for a requested date in JSON format. If there are multiple versions, the latest version with an effective date not later than the requested date is returned. If the effective date for all versions is greater than the requested date, the earliest version is returned. See the previous endpoint for examples.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog"  
import org.joda.time.DateTime;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.Catalogs;

protected CatalogApi catalogApi;

DateTime requestedDate = null;
UUID accountId = null;

Catalogs catalogsJson = catalogApi.getCatalogJson(requestedDate, 
                                                  accountId, 
                                                  requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil

catalog_json = catalog.get_tenant_catalog_json(requested_date,options)
catalogApi = killbill.CatalogApi()

catalog_json = catalogApi.get_catalog_json()
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const catalogJson: AxiosResponse<killbill.Catalog[], any> = await catalogApi.getCatalogJson();
$apiInstance = $client->getCatalogApi();

$catalogJson = $apiInstance->getCatalogJson();

Example Response:

[
    {
      "name": "SpyCarBasic",
      "effectiveDate": "2013-02-08T00:00:00.000+0000",
      "currencies": [
        "USD",
        "GBP"
      ],
      "units": [],
      "products": [
        {
          "type": "ADD_ON",
          "name": "Super",
          "prettyName": "Super",
          "plans": [
            {
              "name": "super-monthly",
              "prettyName": "super-monthly",
              "billingPeriod": "MONTHLY",
              "phases": [
                {
                  "type": "TRIAL",
                  "prices": [],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "DAYS",
                    "number": 30
                  },
                  "usages": []
                },
                {
                  "type": "EVERGREEN",
                  "prices": [
                    {
                      "currency": "GBP",
                      "value": 750
                    },
                    {
                      "currency": "USD",
                      "value": 1000
                    }
                  ],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "UNLIMITED",
                    "number": -1
                  },
                  "usages": []
                }
              ]
            }
          ],
          "included": [],
          "available": []
        },
        {
          "type": "BASE",
          "name": "Standard",
          "prettyName": "Standard",
          "plans": [
            {
              "name": "standard-monthly",
              "prettyName": "standard-monthly",
              "billingPeriod": "MONTHLY",
              "phases": [
                {
                  "type": "TRIAL",
                  "prices": [],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "DAYS",
                    "number": 30
                  },
                  "usages": []
                },
                {
                  "type": "EVERGREEN",
                  "prices": [
                    {
                      "currency": "GBP",
                      "value": 75
                    },
                    {
                      "currency": "USD",
                      "value": 100
                    }
                  ],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "UNLIMITED",
                    "number": -1
                  },
                  "usages": []
                }
              ]
            }
          ],
          "included": [],
          "available": []
        },
        {
          "type": "BASE",
          "name": "Sports",
          "prettyName": "Sports",
          "plans": [
            {
              "name": "sports-monthly",
              "prettyName": "sports-monthly",
              "billingPeriod": "MONTHLY",
              "phases": [
                {
                  "type": "TRIAL",
                  "prices": [],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "DAYS",
                    "number": 30
                  },
                  "usages": []
                },
                {
                  "type": "EVERGREEN",
                  "prices": [
                    {
                      "currency": "GBP",
                      "value": 375
                    },
                    {
                      "currency": "USD",
                      "value": 500
                    }
                  ],
                  "fixedPrices": [],
                  "duration": {
                    "unit": "UNLIMITED",
                    "number": -1
                  },
                  "usages": []
                }
              ]
            }
          ],
          "included": [],
          "available": []
        }
      ],
      "priceLists": [
        {
          "name": "DEFAULT",
          "plans": [
            "sports-monthly",
            "standard-monthly",
            "super-monthly"
          ]
        }
      ]
    }
]

Query Parameters

Name Type Required Default Description
requestedDate string false current date requested date

Response

if successful, returns a status code of 200 and the full catalog for the requested date in JSON format.

Retrieve a list of catalog versions

Return a list of the effective dates for all available catalogs versions for this tenant.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/versions

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/versions"
import org.killbill.billing.client.api.gen.CatalogApi;
protected CatalogApi catalogApi;

UUID accountId = null;

List<DateTime> versions = catalogApi.getCatalogVersions(accountId, requestOptions);
catalog = KillBillClient::Model::Catalog

catalog_versions = catalog.get_tenant_catalog_versions(options)
catalogApi = killbill.CatalogApi()

catalog_versions = catalogApi.get_catalog_versions()
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const catalogVersions: AxiosResponse<string[], any> = await catalogApi.getCatalogVersions();
$apiInstance = $client->getCatalogApi();

$catalogVersions = $apiInstance->getCatalogVersions();

Example Response:

[
  "2013-02-08T00:00:00.000Z"
]

Query Parameters

None.

Response

If successful, returns a status code of 200 and a comma-separated list of ISO date strings giving the effective date for each available catalog version.

Retrieve available base plans

Returns a list of available base products and associated plans. Each object returned specifies a product, a priceList, a planselected from the pricelist, and pricing information for the final phase of the plan.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/availableBasePlans

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/availableBasePlans"   
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.PlanDetail;

protected CatalogApi catalogApi;

UUID accountId = null;

List<PlanDetail> basePlans = catalogApi.getAvailableBasePlans(accountId, requestOptions);
catalog = KillBillClient::Model::Catalog

available_base_plans = catalog.available_base_plans(options)
catalogApi = killbill.CatalogApi()

available_base_plans = catalogApi.get_available_base_plans()
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const availableBasePlans: AxiosResponse<killbill.PlanDetail[], any> = await catalogApi.getAvailableBasePlans();
$apiInstance = $client->getCatalogApi();

$availableBasePlans = $apiInstance->getAvailableBasePlans();

Example Response:

[
  {
    "product": "Sports",
    "plan": "sports-monthly",
    "priceList": "DEFAULT",
    "finalPhaseBillingPeriod": "MONTHLY",
    "finalPhaseRecurringPrice": [
      {
        "currency": "GBP",
        "value": 375
      },
      {
        "currency": "USD",
        "value": 500
      }
    ]
  },
  {
    "product": "Standard",
    "plan": "standard-monthly",
    "priceList": "DEFAULT",
    "finalPhaseBillingPeriod": "MONTHLY",
    "finalPhaseRecurringPrice": [
      {
        "currency": "GBP",
        "value": 75
      },
      {
        "currency": "USD",
        "value": 100
      }
    ]
  }
]

Query Parameters

None.

Response

If successful, returns a status code of 200 and a list of objects representing the available base products and plans.

Retrieve available add-ons for a given product

Returns a list of available add-on products, if any, for a specified base product, and for a specified price list or all price lists. Each object returned specifies a product, a priceList, a plan selected from the pricelist, and pricing information for the final phase of the plan.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/availableAddons

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/availableAddons"  
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.PlanDetail;

protected CatalogApi catalogApi;

String baseProductName = "Bullets";
String priceListName = null;
UUID accountId = null;

List<PlanDetail> availableAddons = catalogApi.getAvailableAddons(baseProductName, 
                                                                 priceListName, 
                                                                 accountId, 
                                                                 requestOptions);
catalog = KillBillClient::Model::Catalog

base_product_name = 'Standard'

available_addons = catalog.available_addons(base_product_name,
                                            options)
catalogApi = killbill.CatalogApi()

available_add_on_plans = catalogApi.get_available_addons(base_product_name='Basic')
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const availableAddOns: AxiosResponse<killbill.PlanDetail[], any> = await catalogApi.getAvailableAddons();
$apiInstance = $client->getCatalogApi();

$baseProductName = 'Standard';

$availableAddOns = $apiInstance->getAvailableAddons($baseProductName);

Example Response:

[
   {
      "product":"Basic",
      "plan":"basic-annual",
      "finalPhaseBillingPeriod":"ANNUAL",
      "priceList":"DEFAULT",
      "finalPhaseRecurringPrice":[
         {
            "currency":"USD",
            "value":10000.0
         }
      ]
   }
]

Query Parameters

Name Type Required Default Description
baseProductName string true none base product name
priceListName string false all price lists price list name

Response

If successful, returns a status code of 200 and a list of objects representing available add-on products.

Delete all versions of a per tenant catalog

Delete all per-tenant catalog versions. The tenant reverts to the system default catalog.

HTTP Request

DELETE http://127.0.0.1:8080/1.0/kb/catalog

Example Request:

curl -v \
    -X DELETE \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "X-Killbill-CreatedBy: demo" \
    -H "X-Killbill-Reason: demo" \
    -H "X-Killbill-Comment: demo" \
    "http://127.0.0.1:8080/1.0/kb/catalog"  
import org.killbill.billing.client.api.gen.CatalogApi;

protected CatalogApi catalogApi;

catalogApi.deleteCatalog(requestOptions);
catalog = KillBillClient::Model::Catalog

catalog.delete_catalog(user,reason,comment,options)
catalogApi = killbill.CatalogApi()

catalogApi.delete_catalog(created_by='demo',
                          reason='reason', 
                          comment='comment')
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const response: AxiosResponse<any> = await catalogApi.deleteCatalog('created_by');
$apiInstance = $client->getCatalogApi();

$xKillbillCreatedBy = "user";
$xKillbillReason = "reason";
$xKillbillComment = "comment";

$apiInstance->deleteCatalog($xKillbillCreatedBy,$xKillbillReason,$xKillbillComment);

Query Parameters

None.

Response

If successful, returns a a status code of 204 and an empty body.

Validate a catalog XML

This endpoint validates an XML catalog.

Following are some of the errors that can be detected via this endpoint:

HTTP Request

POST http://127.0.0.1:8080/1.0/kb/catalog/xml/validate

Example Request:

curl -v \
    -X POST \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Content-Type: text/xml" \
    -H "Accept: application/json" \
    -H "X-Killbill-CreatedBy: demo" \
    -H "X-Killbill-Reason: demo" \
    -H "X-Killbill-Comment: demo" \
    -d @<path_to_catalog_xml> \
    "http://127.0.0.1:8080/1.0/kb/catalog/xml/validate"
import java.nio.file.Files;
import java.nio.file.Path;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.CatalogValidation;
import org.killbill.billing.client.model.gen.CatalogValidationError;

protected CatalogApi catalogApi;

CatalogValidation validation = catalogApi.validateCatalogXml(body, requestOptions);

List<CatalogValidationError> errors = validation.getCatalogValidationErrors();
TODO
catalogApi = killbill.CatalogApi()

xml_catalog = open("H:/killbill/catalog.xml", "r+").read()

catalog_validation_errors = catalogApi.validate_catalog_xml(xml_catalog,
                                                            created_by='demo',
                                                            reason='reason', 
                                                            comment='comment')
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const fs = require('fs');

const body = fs.readFileSync('H:/killbill/catalog.xml').toString();

const catalogValidationErrors: AxiosResponse<killbill.CatalogValidation, any> = await catalogApi.validateCatalogXml(body,'created_by');
$apiInstance = $client->getCatalogApi();

$xKillbillCreatedBy = "user";
$xKillbillReason = "reason";
$xKillbillComment = "comment";

$filename = "H:/killbill/catalog.xml";

$fileContents = file_get_contents($filename);

$catalogValidationErrors = $apiInstance->validateCatalogXml($fileContents,$xKillbillCreatedBy,$xKillbillReason,$xKillbillComment);

Example Response:

{
  "catalogValidationErrors": [
    {
      "errorDescription": "Catalog name 'ExampleCatalog' is different from existing catalog name 'Firearms'"
    }
  ]
}

Request Body

The complete catalog in XML format. Alternative, the path to the catalog XML file can also be specified.

Query Parameters

None.

Response

If successful, returns a status code of 200 and a CatalogValidation object. If validation is successful and there are no validation errors, the CatalogValidation object is empty, otherwise it contains a CatalogValidationError List.

Subscription info

These endpoints return information concerning a particular subscription. They select from the catalog only the items (such as plan, phase, or products) that currently apply to the specified subscription.

Retrieve the phase for a given subscription and date

This API returns information about the current Phase associated with a given subscription. The record returned includes the phase type and information about pricing, duration, and usage.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/phase

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/phase?subscriptionId=8ab101b6-15e8-433b-b4f7-f99eeaa56a77&requestedDate=2018-7-18"    
import org.joda.time.LocalDate;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.Phase;

protected CatalogApi catalogApi;

UUID subscriptionId = UUID.fromString("4c3fd23c-7b15-4acc-811e-fe92ee7fdffd");
LocalDate requestedDate = null;

Phase phase = catalogApi.getPhaseForSubscriptionAndDate(subscriptionId, requestedDate, requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil
subscription_id = 'dc99644c-04a3-49b3-9abc-6e94cffc7e60'

phase = catalog.get_catalog_phase(subscription_id,
                                  requested_date,
                                  options)
catalogApi = killbill.CatalogApi()

subscription_id = 'ad924bca-00f4-4287-82c2-e2932a5f7371'

phase = catalogApi.get_phase_for_subscription_and_date(subscription_id=subscription_id)
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const subscriptionId = 'ad924bca-00f4-4287-82c2-e2932a5f7371';

const phase: AxiosResponse<killbill.Phase, any> = await catalogApi.getPhaseForSubscriptionAndDate(subscriptionId);
$apiInstance = $client->getCatalogApi();

$subscriptionId = 'd28b70f8-6bc7-4cde-b21f-eaf723e146fc';

$phase = $apiInstance->getPhaseForSubscriptionAndDate($subscriptionId);

Example Response:

{
  "type": "TRIAL",
  "prices": [],
  "fixedPrices": [],
  "duration": {
    "unit": "DAYS",
    "number": 30
  },
  "usages": []
} 

Query Parameters

Name Type Required Default Description
subscriptionId string true none subscription id
requestedDate string false current date requested date

Response

If successful, returns a status code of 200 and a record for the current phase.

Retrieve the plan for a given subscription and date

This API returns information about the current Plan associated with a given subscription. The record returned includes the plan name and information for each phase of the plan.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/plan

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/plan?subscriptionId=8ab101b6-15e8-433b-b4f7-f99eeaa56a77&requestedDate=2018-7-18" 
import org.joda.time.LocalDate;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.Plan;

protected CatalogApi catalogApi;

UUID subscriptionId = UUID.fromString("4c3fd23c-7b15-4acc-811e-fe92ee7fdffd");
LocalDate requestedDate = null;

Plan plan = catalogApi.getPlanForSubscriptionAndDate(subscriptionId, requestedDate, requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil
subscription_id = 'dc99644c-04a3-49b3-9abc-6e94cffc7e60'

plan = catalog.get_catalog_plan(subscription_id, 
                                requested_date, 
                                options)
catalogApi = killbill.CatalogApi()

subscription_id = 'ad924bca-00f4-4287-82c2-e2932a5f7371'

plan = catalogApi.get_plan_for_subscription_and_date(subscription_id=subscription_id)
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const subscriptionId = 'ad924bca-00f4-4287-82c2-e2932a5f7371';

const plan: AxiosResponse<killbill.Plan, any> = await catalogApi.getPlanForSubscriptionAndDate(subscriptionId);
$apiInstance = $client->getCatalogApi();

$subscriptionId = 'd28b70f8-6bc7-4cde-b21f-eaf723e146fc';

$plan = $apiInstance->getPlanForSubscriptionAndDate($subscriptionId);

Example Response:

{
  "name": "standard-monthly",
  "prettyName": "standard-monthly",
  "billingPeriod": "MONTHLY",
  "phases": [
    {
      "type": "TRIAL",
      "prices": [],
      "fixedPrices": [],
      "duration": {
        "unit": "DAYS",
        "number": 30
      },
      "usages": []
    },
    {
      "type": "EVERGREEN",
      "prices": [
        {
          "currency": "GBP",
          "value": 75
        },
        {
          "currency": "USD",
          "value": 100
        }
      ],
      "fixedPrices": [],
      "duration": {
        "unit": "UNLIMITED",
        "number": -1
      },
      "usages": []
    }
  ]
}

Query Parameters

Name Type Required Default Description
subscriptionId string true none subscription id
requestedDate string false current date requested date

Response

If successful, returns a status code of 200 and a record for the plan for this subscription.

Retrieve the priceList for a given subscription and date

This API returns information about the current priceList associated with a given subscription. The record returned includes the price list name and the list of plans on this list.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/priceList

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/priceList?subscriptionId=8ab101b6-15e8-433b-b4f7-f99eeaa56a77&requestedDate=2018-7-18"    
import org.joda.time.LocalDate;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.PriceList;

protected CatalogApi catalogApi;

UUID subscriptionId = UUID.fromString("4c3fd23c-7b15-4acc-811e-fe92ee7fdffd");
LocalDate requestedDate = null;

PriceList priceList = catalogApi.getPriceListForSubscriptionAndDate(subscriptionId, requestedDate,requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil
subscription_id = 'dc99644c-04a3-49b3-9abc-6e94cffc7e60'

price_list = catalog.get_catalog_price_list(subscription_id, 
                                            requested_date, 
                                            options)
catalogApi = killbill.CatalogApi()

subscription_id = 'ad924bca-00f4-4287-82c2-e2932a5f7371'

price_list = catalogApi.get_price_list_for_subscription_and_date(subscription_id=subscription_id)
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const subscriptionId = 'ad924bca-00f4-4287-82c2-e2932a5f7371';

const priceList: AxiosResponse<killbill.PriceList, any> = await catalogApi.getPriceListForSubscriptionAndDate(subscriptionId);
$apiInstance = $client->getCatalogApi();

$subscriptionId = 'd28b70f8-6bc7-4cde-b21f-eaf723e146fc';

$priceList = $apiInstance->getPriceListForSubscriptionAndDate($subscriptionId);

Example Response:

{
  "name": "DEFAULT",
  "plans": [
    "sports-monthly",
    "standard-monthly",
    "super-monthly"
  ]
}

Query Parameters

Name Type Required Default Description
subscriptionId string true none subscription id
requestedDate string false current date requested date

Response

If successful, returns a status code of 200 and a record for the price list for this subscription.

Retrieve product for a given subscription and date

This API returns information about the product associated with a given subscription. The record returned includes the product names, available plans, items included, and available add-ons.

HTTP Request

GET http://127.0.0.1:8080/1.0/kb/catalog/product

Example Request:

curl -v \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Accept: application/json" \
    "http://127.0.0.1:8080/1.0/kb/catalog/product?subscriptionId=8ab101b6-15e8-433b-b4f7-f99eeaa56a77&requestedDate=2018-7-18"  
import org.joda.time.LocalDate;
import org.killbill.billing.client.api.gen.CatalogApi;
import org.killbill.billing.client.model.gen.Product;

protected CatalogApi catalogApi;

UUID subscriptionId = UUID.fromString("4c3fd23c-7b15-4acc-811e-fe92ee7fdffd");
LocalDate requestedDate = null;

Product product = catalogApi.getProductForSubscriptionAndDate(subscriptionId, requestedDate,requestOptions);
catalog = KillBillClient::Model::Catalog

requested_date = nil
subscription_id = 'dc99644c-04a3-49b3-9abc-6e94cffc7e60'

product = catalog.get_catalog_product(subscription_id, 
                                      requested_date, 
                                      options)
catalogApi = killbill.CatalogApi()

subscription_id = 'ad924bca-00f4-4287-82c2-e2932a5f7371'

product = catalogApi.get_product_for_subscription_and_date(subscription_id=subscription_id)
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const subscriptionId = 'ad924bca-00f4-4287-82c2-e2932a5f7371';

const product: AxiosResponse<killbill.Product, any> = await catalogApi.getProductForSubscriptionAndDate(subscriptionId);
$apiInstance = $client->getCatalogApi();

$subscriptionId = 'd28b70f8-6bc7-4cde-b21f-eaf723e146fc';

$product = $apiInstance->getProductForSubscriptionAndDate($subscriptionId);

Example Response:

{
  "type": "BASE",
  "name": "Standard",
  "prettyName": "Standard",
  "plans": [],
  "included": [],
  "available": []
}

Query Parameters

Name Type Required Default Description
subscriptionId string true none subscription id
requestedDate string false current date requested date

Response

If successful, returns a status code of 200 and a record for the product for this subscription.

Simple Plan

We provide a more basic level of APIs as a quick way to add a Plan into an existing version of the catalog. The intent is mostly to help getting started with Kill Bill by abstracting away more complex topics such as alignements, rules, ... The functionality is exposed on our admin UI (KAUI) to provide a simple graphical way to configure a simple catalog and get started quickly.

One can directly use our Simple Plan API to add new Plans without the need to create an initial catalog version: If there is no existing catalog version for the tenant, the system will create such an initial version when the first plan is added; otherwise, the system will use the existing active catalog version to add the new plan (but it will not create a new catalog version).

Note that because the Simple Plan API is just an abstraction on top of the more complex XML based APIs, one can start with such Simple Plan API, and then download the resulting XML, and edit such catalog by hand (to add entries, modify default rules, ...).

A simple plan has the following limitations:

Once a simple plan has been uploaded, one can retrieve the associated XML, edit it to configure additional aspects, and then upload a new version of this catalog. So, this functionality can also be a stepping stone to a full catalog configuration.

Add a simple plan

Add a (simple) Plan into the current version of the Catalog associated with the tenant.

HTTP Request

POST http://127.0.0.1:8080/1.0/kb/catalog/simplePlan

Example Request:

curl -v \
    -X POST \
    -u admin:password \
    -H "X-Killbill-ApiKey: bob" \
    -H "X-Killbill-ApiSecret: lazar" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "X-Killbill-CreatedBy: demo" \
    -H "X-Killbill-Reason: demo" \
    -H "X-Killbill-Comment: demo" \
    -d '{ "planId": "basic-annual", "productName": "Basic", "productCategory": "BASE", "currency": "USD", "amount": 1000, "billingPeriod": "ANNUAL", "trialLength": 0, "trialTimeUnit": "UNLIMITED"}' \
    "http://localhost:8080/1.0/kb/catalog/simplePlan"
import java.math.BigDecimal;
import java.util.Collections;

import org.killbill.billing.client.api.gen.CatalogApi;

import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.catalog.api.TimeUnit;
import org.killbill.billing.client.model.gen.SimplePlan;

protected CatalogApi catalogApi;

String planId = "foo-monthly";
String productName = "Foo";
Integer trialLength = 0;

SimplePlan body = new SimplePlan(planId, 
                                 productName, 
                                 ProductCategory.BASE, 
                                 Currency.USD, 
                                 BigDecimal.TEN, 
                                 BillingPeriod.MONTHLY, 
                                 trialLength, 
                                 TimeUnit.UNLIMITED, 
                                 Collections.emptyList());

catalogApi.addSimplePlan(body, requestOptions);
user = 'user'
reason = 'reason'
comment = 'comment'

catalog = KillBillClient::Model::Catalog
simple_plan = KillBillClient::Model::SimplePlanAttributes.new

simple_plan.plan_id          = 'basic-annual'
simple_plan.product_name     = 'Basic'
simple_plan.product_category = 'BASE'
simple_plan.currency         = 'USD'
simple_plan.amount           = 10000.00
simple_plan.billing_period   = 'ANNUAL'
simple_plan.trial_length     = 0
simple_plan.trial_time_unit  = 'UNLIMITED'

catalog.add_tenant_catalog_simple_plan(simple_plan,
                                       user,
                                       reason,
                                       comment,
                                       options)
catalogApi = killbill.CatalogApi()

body = killbill.SimplePlan(plan_id='basic-annual',
                           product_name='Basic',
                           product_category='BASE',
                           currency='USD',
                           amount=10000.00,
                           billing_period='ANNUAL',
                           trial_length=0,
                           trial_time_unit='UNLIMITED')

catalogApi.add_simple_plan(body,
                           created_by='demo',
                           reason='reason', 
                           comment='comment')
const catalogApi: killbill.CatalogApi = new killbill.CatalogApi(config);

const simplePlan: killbill.SimplePlan = {
                                          planId: 'basic-annual',
                                          productName: 'Basic',
                                          productCategory: 'BASE',
                                          currency: 'USD',
                                          amount: 10000,
                                          billingPeriod: 'ANNUAL',
                                          trialLength: 0,
                                          trialTimeUnit: 'UNLIMITED'
                                        };

const response: AxiosResponse<string, any> = await catalogApi.addSimplePlan(simplePlan,
                                                                            'created_by');
$apiInstance = $client->getCatalogApi();

$xKillbillCreatedBy = "user";
$xKillbillReason = "reason";
$xKillbillComment = "comment";

$simplePlan = new SimplePlan();

$simplePlan->setPlanId('basic-annual');
$simplePlan->setProductName('Basic');
$simplePlan->setProductCategory('BASE');
$simplePlan->setCurrency('USD');
$simplePlan->setAmount(10000);
$simplePlan->setBillingPeriod('ANNUAL');
$simplePlan->setTrialLength(0);
$simplePlan->setTrialTimeUnit('UNLIMITED');

$apiInstance->addSimplePlan($simplePlan,$xKillbillCreatedBy,$xKillbillReason,$xKillbillComment);

Request Body

Provides the content for the plan in JSON form. This should be very simple. Note that the "planId" becomes the planName attribute. For example:

{ "planId": "newplan", "productName": "myitem", "productCategory": "BASE", "currency": "USD", "amount": 0, "billingPeriod": "DAILY", "trialLength": 0, "trialTimeUnit": "DAYS", "availableBaseProducts": [] }

Query Parameters

None.

Response

If successful, returns a status code of 201 and an empty body.