## ADDED Requirements ### Requirement: Per-tenant usage is tracked for API calls, active agents, and token issuances The system SHALL track the following usage metrics per tenant per day: `api_calls` (every authenticated API request), `token_issuances` (every successful `POST /oauth2/token`), `active_agents` (count of non-revoked agents at end of day). Usage SHALL be aggregated in memory and flushed to a `usage_events` PostgreSQL table every 60 seconds. #### Scenario: API call increments usage counter - **WHEN** an authenticated tenant makes any API request - **THEN** the tenant's `api_calls` counter for the current day is incremented #### Scenario: Usage is persisted to database on flush interval - **WHEN** 60 seconds elapse since the last flush - **THEN** all in-memory counters are written to the `usage_events` table and reset to zero ### Requirement: Free tier limits are enforced per tenant The system SHALL enforce free tier limits: 10 active agents maximum, 1,000 API calls per day. When a limit is exceeded, the offending request SHALL be rejected with HTTP 429 and a response body indicating which limit was reached and how to upgrade. Limit summaries SHALL be cached in Redis with a 60-second TTL. #### Scenario: Agent registration blocked at free tier limit - **WHEN** a free-tier tenant with 10 active agents calls `POST /agents` - **THEN** the response is HTTP 429 with `{ "error": "free_tier_limit", "limit": "agents", "max": 10, "upgradeUrl": "..." }` #### Scenario: API call blocked after daily limit - **WHEN** a free-tier tenant has made 1,000 API calls today and makes another request - **THEN** the response is HTTP 429 with `{ "error": "free_tier_limit", "limit": "api_calls", "max": 1000, "upgradeUrl": "..." }` #### Scenario: Paid tenant is not rate limited by usage tiers - **WHEN** a paid-tier tenant exceeds free tier thresholds - **THEN** the request is processed normally with no usage-based rejection ### Requirement: Stripe Checkout initiates paid tier subscription The system SHALL expose `POST /billing/checkout` (authenticated) that creates a Stripe Checkout session for the paid tier plan and returns a `checkoutUrl`. The tenant is redirected to Stripe Checkout to complete payment. On success, Stripe sends a `customer.subscription.created` webhook event. #### Scenario: Checkout session created - **WHEN** an authenticated tenant calls `POST /billing/checkout` - **THEN** the response is HTTP 200 with `{ "checkoutUrl": "https://checkout.stripe.com/..." }` #### Scenario: Duplicate subscription prevented - **WHEN** a tenant with an active paid subscription calls `POST /billing/checkout` - **THEN** the response is HTTP 409 with `{ "error": "already_subscribed" }` ### Requirement: Stripe webhooks update tenant subscription state The system SHALL expose `POST /billing/webhook` (Stripe webhook endpoint) that verifies the `stripe-signature` header using `stripe.webhooks.constructEvent()` and processes: `customer.subscription.created` (set tenant to paid), `invoice.payment_succeeded` (extend subscription period), `customer.subscription.deleted` (revert tenant to free tier). All events without valid signatures SHALL be rejected with HTTP 400. #### Scenario: Webhook without valid signature is rejected - **WHEN** `POST /billing/webhook` is called with an invalid or missing `stripe-signature` header - **THEN** the response is HTTP 400 and no state is changed #### Scenario: Subscription created webhook activates paid tier - **WHEN** Stripe sends a valid `customer.subscription.created` event for a tenant - **THEN** the tenant's `subscriptionStatus` is updated to `active` and free tier limits no longer apply #### Scenario: Subscription deleted webhook reverts to free tier - **WHEN** Stripe sends a valid `customer.subscription.deleted` event - **THEN** the tenant's `subscriptionStatus` is updated to `cancelled` and free tier limits are re-enforced ### Requirement: Billing is feature-flag gated All billing enforcement and Stripe integration SHALL be gated behind the `BILLING_ENABLED` environment variable. When `BILLING_ENABLED=false`, free tier limits are not enforced, all tenants have paid-tier access, and Stripe webhook endpoint returns HTTP 200 without processing. Usage metering continues regardless of this flag. #### Scenario: Billing disabled — no limits enforced - **WHEN** `BILLING_ENABLED=false` and a free-tier tenant has 11 active agents - **THEN** agent registration succeeds without HTTP 429