Mapping store products to entitlements
A store product id and an entitlement id answer different questions. Here is why they diverge, how several products collapse into one entitlement, and how the mapping between them gets configured.
Why product ids diverge and entitlement ids should not
A store product id names a specific thing you sell: com.yourapp.pro_monthly on Google Play, pro_monthly on the App Store, a Stripe price id for a web checkout. Each store assigns its own id format, and within one store the id set fragments further — a monthly plan and an annual plan are two different product ids, a US price tier and an EU price tier can be two different product ids, and a plan you renamed last year may still have old purchasers on the old id. None of that is a Tierux detail; it is how Google Play and the App Store model catalogs.
An entitlement id names a feature your backend gates on — pro, unlimited_exports — and it should stay boring and stable regardless of which of the products above someone bought. The read path only ever asks about the entitlement: GET /api/v1/entitlements/{userId}/{entitlementId} (see chapter 3) never sees com.yourapp.pro_monthly or pro_monthly — it sees pro. Whatever your gating code looks like, it should not grow a branch per store or per billing period.
The mapping is the layer that collapses the first kind of id into the second. Tierux stores it as a small record — a store product id, the entitlement id it grants, and a product type (subscription or one_time) — and every verification and every notification-driven state change looks the product id up in that record before touching an entitlement. When Real-Time Developer Notifications or App Store Server Notifications carry only a package name and a product id (see chapter 4), this is the lookup that turns "a purchase changed" into "which entitlement changed." A notification whose product id resolves to nothing is a purchase this backend never mapped, and is a no-op rather than a guess.
One entitlement, many products
The mapping is many-to-one on purpose: several store product ids can point at the same entitlement id, and nothing about the entitlement id itself records which one was actually purchased. This is exactly the shape you want for the cases that come up on every real paywall:
- Monthly vs. annual.
pro_monthlyandpro_annualare different Play or App Store products with different prices and renewal periods — both map to the entitlementpro. The subscriber's backend-facing access does not care which billing period they chose. - Regional pricing. A store may expose separate product ids for region-specific price points. Each one still maps to the same entitlement so a subscriber in any region reads back the same
prostate. - Cross-store parity. The Google Play id and the App Store id for what your product page calls "Pro" are unrelated strings in unrelated catalogs. Mapping both to
prois what lets one gating check work for a user regardless of which store they bought from.
What the mapping does not do is remember which specific product a subscriber holds once the entitlement is granted — that detail lives on the purchase record, not the mapping. The mapping is a lookup table (product id → entitlement id, product type), not a per-user assignment.
How the mapping gets configured
The mapping is created and updated with the create_product_mapping MCP tool. Its input is { productId, entitlementId, productType, appId? } — productType is subscription or one_time, and appId can be omitted if the calling session only has one app. Like every write-safe Tierux MCP tool, it is a two-call flow: call it without a confirmToken and it returns a preview plus a confirmToken; call it again with that token to persist the mapping. Nothing is written on the first call, so an agent narrating "map pro_monthly to pro" is not the same as it having happened yet — the human approves the preview first. get_product_mappings is the read-only counterpart, returning every mapping configured for the app so you (or the agent) can check what already exists before adding to it.
The tool's own description scopes it to "a Google Play productId," and the preview step reflects that framing: for a subscription-type mapping, it looks the product id up against the app's created Google Play catalog and attaches an advisory warning if it isn't found there — worded as exactly that, an advisory, because a mapping is allowed to precede product creation. In practice the lookup only understands the Play catalog, so mapping an App Store product id through this same tool will surface that "no Play product found" warning even though the mapping itself is not Play-specific — the entitlement lookup on both the verification and notification paths resolves a product id to an entitlement id without caring which store it came from. Treat the warning as what it says: not found in the Play catalog, not invalid.
There is no separate dashboard form for authoring a mapping today — the mapping list surfaces in the dashboard for reference (it feeds the same integration checklist that confirms "product mapping exists" before you test a purchase), but the write path is the MCP tool, whether an AI agent is driving it conversationally or you are calling it directly yourself, preview then confirm.
Feature: Entitlement mapping
The product page for what this chapter describes — configurable server-side, one mapping per product, checked in a single entitlement read.
Docs: MCP setup
Connecting an AI agent to the Tierux MCP server, and the confirm-token pattern every write tool — including this one — follows.
Glossary: MCP
What the Model Context Protocol is, and why Tierux exposes mapping, product, and paywall configuration through it.
Entitlements in one REST call
Free tier — unlimited apps, 1 paywall. No credit card, no revenue share.
Start free