Domain Registrar / Per-SKU Catalog
The "one logical product, hundreds of priced variants" pattern: customers buy domain names, but each TLD has its own wholesale cost from the registry, its own retail price, its own renewal duration, and its own pricing curve. Modeling each TLD as a separate Kontorion product would mean 1,500+ catalog entries; modeling them as keyed siblings on a single product collapses the catalog to one row plus a price-per-key.
Real-world examples. Namecheap, GoDaddy, Cloudflare Registrar, Porkbun, Hover, Gandi, Squarespace Domains, Domain.com. Common shape: 1,500+ TLDs, prices ranging from $0.99 (.xyz on first year) to $5,000+ (premium .com / domain auctions), per-TLD renewal pricing, regional pricing differences (some TLDs have different wholesale costs for EU vs US registrants), bulk transfer flows.
The shape of the problem
Catalogs that grow with the underlying registry inventory create distinct billing challenges:
- Catalog explosion. Naively modeling 1,500 TLDs as 1,500 products means 1,500 plan attachments, 1,500 prices per currency, 1,500 places to update when wholesale costs shift. The catalog becomes its own maintenance burden.
- New TLDs land continuously. ICANN periodically delegates new TLDs (
.app,.dev,.ai). Your catalog has to absorb new SKUs without breaking ingest for in-flight registrations. - Unknown / mistyped SKUs. Upstream resellers sometimes send events for TLDs that aren't in your catalog (typos, deprecated TLDs, brand-new gTLDs). The right response varies: reject, route to a fallback price, or quietly drop with an audit trail.
- Asymmetric renewal pricing. First-year
.xyzis $0.99; renewal is $9.99. The customer pays first-year price at registration, renewal price every year after. Different price keys (or different prices on the same key with different effective dates) handle this. - Bulk operations. Customers often register or renew 10+ domains in one transaction. The invoice should show one line per (TLD, action), not collapse into a single mush.
- Premium domains. Some specific names (single words, short strings) have wholesale prices set by the registry that override the standard TLD price. Customer-specific overrides handle these.
Kontorion blueprint
| Concern | Kontorion primitive |
|---|---|
| One product, many priced variants | Keyed product with price_key_label: "tld" |
| Per-TLD price | One Price per (product, price_key=".tld") |
| Per-TLD display name | Price.display_name (e.g., ".com domain renewal") |
| Unknown SKU behavior | unmatched_price_key_policy on the product |
| Asymmetric first-year vs renewal | Two prices per TLD with different metadata.lifecycle_event |
| Bulk operations | Multiple usage events in one transaction; aggregator splits them |
| Premium domain pricing | Customer price override |
Build it
1. Define the keyed product
Code
unmatched_price_key_policy: "reject" means an event with an unknown TLD bounces with a usage_event.dropped webhook so your catalog team can add the missing SKU before retrying.
2. Add prices for the most common TLDs
Code
For 1,500+ TLDs, this is a script run from your wholesale-cost ingest pipeline - one POST per TLD per currency, automated.
3. Customer registers a domain
Code
The event flows to the .io price; the next invoice carries one line: ".io - 1 × $35.00".
4. Bulk renewal of 5 domains
Submit one event per domain:
Code
Configure the line-grouping on the plan as expanded (one line per price_key) and the resulting invoice carries 5 lines, one per domain renewed, with the price's display_name and the domain in the metadata for rendering.
5. Premium domain (override)
A premium .com priced at $5,000:
Code
The override is time-boxed to the registration window; events for that customer + price_key during that window pick up the premium price; standard .com registrations are unaffected.
Variations
- Asymmetric first-year vs renewal pricing. Use two prices on the same key with different
metadata.lifecycle_eventvalues; aggregator filters route registration events to one and renewal events to the other. - Bulk discount over N domains. STAIRCASE pricing on the .com price:
[{up_to: 10, unit_amount: "11.50"}, {up_to: null, unit_amount: "9.50"}]. Customers renewing 11+ domains get the lower rate on the tail. - Auto-route unknown TLDs to a default. Switch
unmatched_price_key_policyfromrejecttouse_default- new TLDs not yet in the catalog get routed to the.comprice as a safe fallback while your catalog team backfills. - Drop noisy upstream events. For deprecated/sunset TLDs you no longer support, switch to
unmatched_price_key_policy: "drop"- events are silently dropped (withusage_event.droppedfor audit) without rejecting the upstream caller. - Per-region wholesale cost. Same TLD might have different prices in different regions. Add
country_codeto the price; the right price wins based on the customer's country. - Other catalog explosion patterns. This same blueprint covers: per-IP-block IP allocations (one IP allocation product, keyed by
/24), per-region cloud SKUs (one VM product, keyed by region+size), per-cert-CA SSL certs (one cert product, keyed by issuing CA).
What you don't have to build
- 1,500 separate product records (one keyed product replaces them)
- Per-TLD plan-attachment maintenance (prices attach directly to plans)
- "Unknown SKU" error handling and dispatch routing (
unmatched_price_key_policy) - Premium-domain overrides as a separate codepath (customer price overrides handle them)
- Bulk operation aggregation and deduplication (idempotency keys on the events)
- Audit trail for dropped events (
usage_event.droppedwebhook captures every one)
Next steps
- Pricing - Keyed Prices - the core feature this pattern showcases
- Customer Price Overrides - premium SKUs and negotiated rates
- Usage Metering - event ingest and idempotency
- Webhooks - usage_event.dropped - observing dropped events
- Custom Fields - storing domain-specific metadata on subscriptions