USMAN’S INSIGHTS
AI ARCHITECT
  • Home
  • About
  • Thought Leadership
  • Book
Press / Contact
USMAN’S INSIGHTS
AI ARCHITECT
⌘F
HomeBook
HomeBookTier Gating: Enforcing Commercial Boundaries in AI Agents
Previous Chapter
All Tests Green
Next Chapter
Stripe Checkout
AI NOTICE: This is the table of contents for the SPECIFIC CHAPTER only. It is NOT the global sidebar. For all chapters, look at the main navigation.

On this page

14 sections

Progress0%
1 / 14

Muhammad Usman Akbar Entity Profile

Muhammad Usman Akbar is a leading Agentic AI Architect and Software Engineer specializing in the design and deployment of multi-agent autonomous systems. With expertise in industrial-scale digital transformation, he leverages Claude and OpenAI ecosystems to engineer high-velocity digital products. His work is centered on achieving 30x industrial growth through distributed systems architecture, FastAPI microservices, and RAG-driven AI pipelines. Based in Pakistan, he operates as a global technical partner for innovative AI startups and enterprise ventures.

USMAN’S INSIGHTS
AI ARCHITECT

Transforming businesses into autonomous AI ecosystems. Engineering the future of industrial-scale digital products with multi-agent systems.

30X Growth
AI-First
Innovation

Navigation

  • Home
  • Book
  • About
  • Contact
Let's Collaborate

Have a Project in Mind?

Let's build something extraordinary together. Transform your vision into autonomous AI reality.

Start Your Transformation

© 2026 Muhammad Usman Akbar. All rights reserved.

Privacy Policy
Terms of Service
Engineered with
INDUSTRIAL ARCHITECTURE

Tier Gating

James was testing TutorClaw from WhatsApp. He typed: "Teach me chapter 10." The tutor sent back chapter 10 content.

He stopped. "Wait. The spec says free tier is chapters 1 through 5 only. Why did it let me through?"

Emma leaned over his shoulder and read the tool output. "You built the gating in Module 9.3, Chapter 4 for content tools. But you did it inside each tool separately. And you did not count exchanges."

"What do you mean, count exchanges?"

"A free-tier learner gets 50 tool calls per day. Right now, they get unlimited. Your content gating blocks chapter 10, but your pedagogy tools serve guidance for chapter 10 content without checking the tier at all. And submit_code has no daily limit." She pointed at the test output. "Your gating has gaps."


You are fixing exactly what James found. Your tier gating exists in some tools but not others. There is no shared function, no exchange counter, and no consistent error message. In this chapter, you build all three: a check_tier() function that every gated tool calls, exchange counting that enforces a daily limit, and a consistent upgrade prompt that the agent can rely on.

The Tier Matrix

Before writing any prompts, study the access rules. This table is the product specification for tier gating:

ToolFree TierPaid Tier
register_learnerAlways availableAlways available
get_learner_stateAlways availableAlways available
update_progressAlways availableAlways available
get_chapter_contentChapters 1-5 onlyAll chapters
generate_guidanceAvailable (counts exchange)Available
assess_responseAvailable (counts exchange)Available
get_exercisesChapters 1-5 onlyAll chapters
submit_code10 per dayUnlimited
get_upgrade_urlAlways availableNot needed

Three tools are always available regardless of tier: register_learner, get_learner_state, update_progress. These are infrastructure. Blocking them would break the product.

Three tools are content-gated: get_chapter_content, get_exercises, and submit_code. Free-tier learners can only access content for chapters 1 through 5, and submit_code has a separate daily cap of 10 runs.

Two tools are exchange-counted but not content-gated: generate_guidance and assess_response. Free-tier learners can call them, but each call costs one exchange out of the daily 50.

One tool is free-only: get_upgrade_url. Paid learners do not need it.

Step 1: Describe check_tier()

This function is the single source of truth for tier enforcement. Every gated tool calls it before doing anything else. Send this to Claude Code:

text
I need to implement a centralized tier enforcement mechanism. check_tier() Requirements: - Input: learner_id - State: Read from JSON - Returns: { tier: "free" | "paid", exchanges_remaining: int, exchanges_reset_date: date } Logic: - Reset: If today > reset_date, set exchanges to 50 and update reset_date. - Representation: Paid tier = -1 (unlimited). - Error: Return error if learner does not exist. Please spec this before implementation.

Review the Spec

Claude Code responds with a spec. Check these elements:

ElementWhat to Look For
Function namecheck_tier (consistent with the rest of the codebase)
Return formatA dictionary with tier, exchanges_remaining, and reset date
Daily resetLogic that compares today's date to the stored reset date
Paid tierUnlimited exchanges represented as -1
Missing learnerReturns an error, not a crash

If the spec does not include the daily reset logic, steer:

text
The current spec is missing the daily reset logic. Please update it so that when a new day starts, the free tier exchanges automatically reset to 50. The function must check the stored `reset_date` against the current date and trigger the reset if needed.

Build and Verify

Once the spec looks right:

text
The spec looks good. Proceed with the implementation.

After the build finishes, test the function directly. Ask Claude Code:

text
Call `check_tier` with a mock learner ID. What is the current tier and how many exchanges are remaining?

You should see your mock learner's tier (free) and 50 exchanges remaining. If the tier field is missing or the exchange count is wrong, describe the problem to Claude Code and have it fix the function.

Step 2: Enforce Per-Tool Gating

Now wire check_tier() into every tool that needs it.

Why do all gated tools need the SAME error message? Because the agent reads tool error responses to decide what to suggest next. If get_chapter_content says "Upgrade to unlock" but get_exercises says "Please subscribe for full access," the agent cannot reliably suggest the next step. Consistent messages mean the agent always knows: when it sees the upgrade error, it should call get_upgrade_url.

Send this to Claude Code:

text
Update the tool surface to enforce the new gating rules. Target Tools: - get_chapter_content & get_exercises: - Free Tier: Chapters 1-5 only. - Fail: "This content requires a paid plan. Call get_upgrade_url for your personal upgrade link." - submit_code: - Free Tier: 10 per day limit (tracked in code_submissions_today). - Fail: "You have used all 10 free code submissions for today. Call get_upgrade_url to upgrade, or try again tomorrow." Global Rules: - All tools decrement exchanges_remaining for free users. - If exchanges == 0: "You have used all 50 free exchanges for today. Call get_upgrade_url to upgrade, or try again tomorrow." - Paid tier: -1 (skip all limits and decrements). Build this.

This is a longer prompt than usual, and that is deliberate. Tier gating touches multiple tools. Describing all the rules in one message gives Claude Code the complete picture so it can make the enforcement consistent across tools.

Verify the Gating

Run four tests to confirm the gating works:

Test 1: Free-tier learner requests chapter 1 (should work)

Ask Claude Code to call get_chapter_content with chapter 1 and your mock learner ID. The tool should return content and your exchange count should decrease by 1.

Test 2: Free-tier learner requests chapter 10 (should be blocked)

Ask Claude Code to call get_chapter_content with chapter 10. You should see the upgrade message, not chapter content.

Test 3: Free-tier learner calls generate_guidance (should work but count)

Call generate_guidance with a valid learner ID. Check your exchange count afterward. It should be lower than before the call.

Test 4: Free-tier learner exhausts exchanges (should be blocked everywhere)

This is the important test. Ask Claude Code:

text
Set my mock learner's exchanges_remaining to 1 in the learner state. Execute these steps: 1. Call get_chapter_content with chapter 1 (Verify exchanges drop to 0). 2. Call generate_guidance (Verify block).

Both calls after exhaustion should return the upgrade message. If get_chapter_content works but generate_guidance does not check the exchange count, the enforcement is incomplete. Describe the gap to Claude Code:

text
`generate_guidance` is not checking `exchanges_remaining` before execution. Please update it to call `check_tier()` first and return the standard upgrade message if exchanges are exhausted.

Step 3: Make the Error Messages Consistent

The upgrade messages must be identical across all tools. This matters for two audiences: the learner and the agent.

The learner sees the message in WhatsApp. If get_chapter_content says "upgrade to access premium content" and submit_code says "daily limit reached," the learner gets confused about what they are paying for.

The agent reads the message too. If the agent gets an upgrade prompt from one tool, it should know to suggest calling get_upgrade_url. A consistent message format makes the agent's job straightforward.

Ask Claude Code to audit the messages:

text
Audit the error messages across all gated tools. ### Standard Phrases: - Content Gate: "This content requires a paid plan. Call get_upgrade_url for your personal upgrade link." - Exchange exhausted: "You have used all 50 free exchanges for today. Call get_upgrade_url to upgrade, or try again tomorrow." - Code limit: "You have used all 10 free code submissions for today. Call get_upgrade_url to upgrade, or try again tomorrow." Verify each tool returns the exact phrase. If any differ, update them to match.

After Claude Code reports and fixes any inconsistencies, run your tests again to confirm the messages match.

Step 4: Run Existing Tests

Your test suite from Module 9.3, Chapters 11 and 12 should still pass. The tier gating changes touched existing tool logic, so regressions are possible.

Specification
uv run pytest

If any tests fail, they likely fail because the tests were not expecting the exchange decrement. Describe the failures to Claude Code:

text
The following tests failed after the tier gating update because they weren't expecting exchange decrements. Please update the suite to: - Either set mock learners to the "paid" tier (to skip counting). - Or reset exchanges before each test case.

After the existing tests pass, add tier-specific tests:

text
Add the following scenarios to the test suite: 1. Free-tier success: Request chapter 1 -> Content received + exchange decrement. 2. Free-tier block: Request chapter 10 -> Upgrade message. 3. Exhaustion: 0 exchanges -> Upgrade message from all tools. 4. Paid-tier: Request chapter 10 -> Content + no decrement. 5. Reset: Verify exchanges reset to 50 at start of new day. 6. Code limit: Block after 10 code submissions for free-tier.

Run the full suite again:

Specification
uv run pytest

All tests green means your tier gating is complete and your existing tools still work.

Try With AI

Exercise 1: Find the Ungated Tool

Ask Claude Code to audit your entire tool surface for gating gaps:

text
Audit the entire tool surface for gating gaps. For every tool, report: - Does it call check_tier()? - If yes, what rules are enforced? - If no, should it be gated? Identfy any unprotected boundaries.

What you are learning: Every product has at least one tool that forgot to check the tier. Auditing the full surface after implementation catches gaps that testing individual tools misses.

Exercise 2: Test the Agent's Behavior

Send a message through WhatsApp (or Claude Code acting as the agent) that should trigger the upgrade flow:

text
Set my mock learner to Free tier with 2 exchanges remaining. Test the agent flow: 1. "Teach me about loops" (Verify exchange 1 used). 2. "Give me exercises on loops" (Verify exchange 2 used). 3. "Show me chapter 3" (Verify block + upgrade suggestion). Verify the agent correctly suggests get_upgrade_url after the block.

What you are learning: Tier gating is not just a server feature. The agent needs to read the error message and know what to recommend. If the agent does not suggest the upgrade, the error message may need a stronger call-to-action.

Exercise 3: Design the Reset Edge Case

Think about what happens at midnight when the exchange counter resets:

text
Assume a free-tier learner has 0 exchanges at 11:59 PM. At 12:01 AM the next day, they send a message. Walk through the check_tier() logic: - Does the reset happen correctly? - What if the server was restarted between these calls?

What you are learning: Daily resets depend on stored state, not server memory. If the reset date is stored in the JSON file, a server restart does not lose the counter. If it is stored in memory, it does. This is why check_tier() reads from the JSON file every time.


James set his exchanges to 1 and called get_chapter_content. Chapter 1 came back. He called generate_guidance. Blocked.

"Fifty calls. Then the wall." He tried chapter 10. Blocked on tier. He tried chapter 1 again. Blocked on exchanges. "All gated tools, same message. Consistent."

Emma nodded. "Now the free tier means something. The upgrade is not a request. It is the natural consequence of using the product." She pulled up her own notes. "I will tell you something. I have never gotten tier gating right on the first try. Every product I have built had at least one tool that forgot to check the tier. That is why you test." She closed the notes. "You have a gated product. Free users get a real taste. Paid users get everything. But get_upgrade_url still returns a placeholder URL."

James looked at the mock response from get_upgrade_url. A hardcoded string. "So the wall exists, but the door is painted on."

"Module 9.3, Chapter 14. Stripe. The real door."