Catalog

Create and map store products to access tiers, with explicit handling for consumables and purchases sold without access.

A catalog product represents one store SKU, and on Google Play one base plan. Apple and Google products are separate rows; map them to the same entitlement identifier when they unlock the same access tier.

Authentication#

The app-scoped management routes below accept a dashboard session, an authorized MCP token, or an owner/admin-issued setup token. They do not accept an SDK publishable key or a backend secret key. Setup tokens expire after 24 hours. Use a token scoped to this app; never put it in a mobile app.

For backend read-only catalog access with a secret key, use GET /v1/catalog. For a customer's actual access, use GET /v1/customers/{appUserId}/entitlements, not the catalog's entitlement definitions.

Retrieve the catalog#

GET /v1/apps/{appId}/catalog

bash
curl "https://api.cashsdk.com/v1/apps/$CASHSDK_APP_ID/catalog" \
  -H "Authorization: Bearer $CASHSDK_SETUP_TOKEN"

The response contains products, entitlements, and groups. Product fields include:

FieldMeaning
idGenerated CashSDK row ID; use this in PATCH paths
identifierExact store product identifier
storeapp-store or play-store
basePlanIdPlay base-plan ID, or null
typeauto_renewable, non_renewing, non_consumable, or consumable
durationSubscription period such as P1M, or null
entitlementIdsCashSDK entitlement row IDs, not identifiers
soldWithoutAccessExplicit acknowledgement that an unmapped purchase intentionally grants no access
statusactive products participate in the mapping readiness check
price, currencyCatalog price in minor units and currency; display the device store's localized price

Catalog entitlements are definitions with id, identifier, name, and rank. They are not a customer's active entitlement snapshot.

Create an entitlement first#

POST /v1/apps/{appId}/catalog/entitlements

bash
curl -X POST "https://api.cashsdk.com/v1/apps/$CASHSDK_APP_ID/catalog/entitlements" \
  -H "Authorization: Bearer $CASHSDK_SETUP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identifier":"pro","name":"Pro","rank":1}'

Save the returned id for mapping. Your app checks the identifier (pro).

Create a product#

POST /v1/apps/{appId}/catalog/products

json
{
  "identifier": "com.example.pro.monthly",
  "store": "app-store",
  "type": "auto_renewable",
  "duration": "P1M",
  "entitlementIds": ["ENTITLEMENT_ROW_ID"]
}

For Play, use store: "play-store" and the exact basePlanId when applicable. Create a separate row for each base plan. The unique identity is app + store + identifier + base plan; reusing it returns 409.

Active subscriptions, passes and lifetime products require an entitlement mapping. Consumables credit balances and need no mapping. For an intentional tip or donation, set soldWithoutAccess: true; do not use this to bypass an unfinished access setup. The flag does not remove existing entitlement links or suppress their grants.

Map an imported product#

PATCH /v1/apps/{appId}/catalog/products/{productId}

Sync can import products before their access tiers are known. Read the catalog, select the exact row (including its Play base plan), and PATCH it instead of creating a duplicate:

json
{ "entitlementIds": ["ENTITLEMENT_ROW_ID"] }

entitlementIds replaces the entire mapping set. Include existing IDs when adding another tier. Other omitted fields remain unchanged. An agent can do this through update_product with productId and entitlementIds.

Readiness and safe edits#

An active access product without a mapping returns HTTP 400 with code: "entitlements_required", affected product identifiers, and remediation when:

  • creating or editing that product;
  • deleting its last linked entitlement;
  • saving app settings whose resulting environment is Production, including going live.

The dashboard's Products → Needs mapping filter identifies these products. The checklist and both store diagnostics exempt consumables, inactive rows and products explicitly sold without access.

Existing Production apps are not automatically switched to Sandbox, and store sync remains allowed to import unmapped rows. Fix the reported mapping before launching those products. These readiness checks do not certify store credentials, receipt settlement, notifications, or a real purchase on a device.

Next steps#