Files
sentryagent-idp/openspec/changes/phase-4-developer-growth/specs/billing-metering/spec.md
SentryAgent.ai Developer b0f70b7ac4 feat(openspec): Phase 4 Developer Growth & Go-to-Market Readiness
OpenSpec change: phase-4-developer-growth (spec-driven, 4/4 artifacts)

6 workstreams, 90 implementation tasks, delivery sequence:
WS1 → WS2 + WS3 (parallel) → WS4 → WS5 → WS6

Workstreams:
1. Production Hardening — ioredis rate limiting, DB pool tuning, /health/detailed, k6 load tests
2. Developer Portal — Next.js 14, Swagger UI explorer, onboarding wizard, pricing/SDK pages
3. CLI Tool — sentryagent npm CLI, 5 commands, shell completion
4. Agent Marketplace — public searchable registry powered by existing agent/DID infrastructure
5. GitHub Actions — register-agent + issue-token Actions via OIDC (no stored secrets)
6. Billing & Usage Metering — Stripe Checkout, webhook-driven state, free tier enforcement

New capabilities (8 specs): production-hardening, developer-portal, cli-tool,
agent-marketplace, github-actions, billing-metering (+delta: web-dashboard, monitoring)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 04:00:34 +00:00

4.4 KiB

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