Analytics & Billing
Plan tiers with resource limits, Stripe subscription management, usage tracking with 30-day rolling windows, materialized views for analytics, and webhook-driven tier changes.
Analytics & Billing
AltBase includes a billing system integrated with Stripe for subscription management and usage tracking, plus an analytics engine that provides materialized views with configurable refresh strategies. Together they give you plan enforcement, upgrade flows, usage summaries, and pre-computed analytics queries.
Overview
Every AltBase project runs on a plan tier (Free, Pro, or Enterprise) that determines rate limits, connection caps, query timeouts, and function resource limits. The billing system handles tier upgrades through Stripe Checkout, processes subscription lifecycle events via webhooks, and tracks per-project usage over a rolling 30-day window.
The analytics engine complements billing with materialized views: define a SQL aggregation query with a refresh interval, and AltBase caches the result in memory for sub-millisecond reads. This is useful for dashboards, reporting, and any scenario where live aggregation queries would be too expensive on the primary database.
Key Concepts
Plan tiers define the resource limits for every project. Free tier is available at no cost with conservative limits. Pro tier unlocks higher throughput and longer timeouts. Enterprise tier provides the highest limits with dedicated database isolation.
Stripe integration manages the payment lifecycle. AltBase creates Stripe Checkout sessions for upgrades, stores the Stripe customer ID and subscription ID on the organization, and processes tier changes in real time via webhooks.
Usage tracking records API calls, storage bytes, bandwidth, and function invocations per project. Metrics are aggregated over a 30-day rolling window and accessible via the usage endpoint.
Materialized views are named SQL queries that run on a schedule and cache their results in a concurrent DashMap. Create a view with a refresh interval (5m, 1h, 1d, or manual), and subsequent reads return the cached result instantly.
Webhook-driven tier changes ensure subscription state stays in sync with Stripe. When a user completes checkout, upgrades, downgrades, or cancels, Stripe sends a webhook that updates the organization's tier immediately. No polling required.
API Reference
Billing Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /billing/v1/checkout | Create a Stripe Checkout session for Pro upgrade | Service Key |
GET | /billing/v1/subscription | Get current subscription status | Service Key |
POST | /billing/v1/subscription/cancel | Cancel the active subscription | Service Key |
GET | /billing/v1/usage | Get 30-day usage summary | Service Key |
POST | /billing/v1/webhooks/stripe | Stripe webhook receiver | Stripe Signature |
Analytics Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/projects/{project_id}/views | Create a materialized view | Service Key |
GET | /analytics/v1/{view_name} | Query a materialized view | API Key |
Plan Tier Limits
| Resource | Free | Pro | Enterprise |
|---|---|---|---|
| Read requests / min | 60 | 600 | 6,000 |
| Write requests / min | 30 | 300 | 3,000 |
| WebSocket connections | 200 | 5,000 | 50,000 |
| Query timeout | 5s | 15s | 30s |
| API engine embed depth | 2 | 3 | 5 |
| Function fuel (Wasm) | 100M | 1B | 10B |
| Function memory | 16 MB | 64 MB | 256 MB |
| Function wall clock | 5s | 15s | 60s |
| Function fetch calls | 5 | 25 | 100 |
| Database isolation | Shared schema | Shared schema | Dedicated database |
Usage Summary Fields
| Field | Type | Description |
|---|---|---|
api_calls | i64 | Total API calls in the 30-day period |
storage_bytes | i64 | Total storage used across all buckets |
bandwidth_bytes | i64 | Total bandwidth consumed (uploads + downloads) |
function_invocations | i64 | Total function executions |
period_start | DateTime | Start of the 30-day window |
period_end | DateTime | End of the 30-day window |
Code Examples
Create a Stripe Checkout session
curl -X POST http://localhost:3000/billing/v1/checkout \
-H "Authorization: Bearer $SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{
"success_url": "https://myapp.com/billing/success",
"cancel_url": "https://myapp.com/billing/cancel"
}'
# Response: {"url": "https://checkout.stripe.com/c/pay_..."}
Check subscription status
curl http://localhost:3000/billing/v1/subscription \
-H "Authorization: Bearer $SERVICE_KEY"
# Active: {"status": "active", "plan": "pro", "current_period_end": "2026-04-23T00:00:00Z"}
# Free: {"status": "free", "message": "No active subscription"}
Get usage summary
curl http://localhost:3000/billing/v1/usage \
-H "Authorization: Bearer $SERVICE_KEY"
# Response:
# {
# "api_calls": 142857,
# "storage_bytes": 5368709120,
# "bandwidth_bytes": 10737418240,
# "function_invocations": 8420,
# "period_start": "2026-02-21T00:00:00Z",
# "period_end": "2026-03-23T00:00:00Z"
# }
Cancel a subscription
curl -X POST http://localhost:3000/billing/v1/subscription/cancel \
-H "Authorization: Bearer $SERVICE_KEY"
# Response: {"message": "Subscription cancelled"}
Create a materialized view
curl -X POST http://localhost:3000/v1/projects/$PROJECT_ID/views \
-H "Authorization: Bearer $SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "daily_orders",
"source_table": "orders",
"query": "SELECT date_trunc('\''day'\'', created_at) as day, count(*) as total, sum(amount) as revenue FROM orders GROUP BY 1 ORDER BY 1 DESC LIMIT 30",
"refresh": "1h"
}'
Query a materialized view
curl http://localhost:3000/analytics/v1/daily_orders \
-H "Authorization: Bearer $ANON_KEY"
# Response:
# {
# "view": "daily_orders",
# "rows": [
# {"day": "2026-03-23", "total": 145, "revenue": 12450.00},
# {"day": "2026-03-22", "total": 132, "revenue": 11280.50}
# ],
# "refreshed_at": "2026-03-23T10:00:00Z"
# }
Configuration
Billing Environment Variables
| Variable | Description |
|---|---|
STRIPE_SECRET_KEY | Stripe API secret key (test or live mode) |
STRIPE_WEBHOOK_SECRET | Webhook endpoint signing secret for signature verification |
STRIPE_PRICE_PRO_MONTHLY | Stripe price ID for the Pro monthly plan |
If STRIPE_SECRET_KEY is not set, the billing endpoints return 503 Service Unavailable with the message "Billing is not configured."
Billing Data Model
The billing system stores Stripe identifiers on the organizations table in the control plane database.
| Column | Type | Description |
|---|---|---|
stripe_customer_id | VARCHAR | Stripe customer ID (created on first checkout) |
stripe_subscription_id | VARCHAR | Active Stripe subscription ID |
Analytics Data Model
| Field | Type | Description |
|---|---|---|
name | String | View name, used in the query URL path |
source_table | String | Table the aggregation runs against |
query | String | SQL aggregation query |
refresh | String | Refresh strategy: manual, 5m, 1h, 1d |
Cached view results are stored in a DashMap<String, CachedView> in memory. Each CachedView contains the rows (as serde_json::Value) and a refreshed_at timestamp.
Rate Limiting Configuration
Rate limiting uses Redis with a fixed-window counter. The key format is rl:{project_id}:{read|write}:{window_start} with a 60-second window.
| Response Header | Description |
|---|---|
X-RateLimit-Limit | Max requests for the current window |
X-RateLimit-Remaining | Requests remaining in the window |
X-RateLimit-Reset | Unix timestamp when the window resets |
When a project exceeds its tier's rate limit, the server returns 429 Too Many Requests with a Retry-After: 60 header.
Set ATLAS_RATE_LIMIT_DISABLED=true to disable rate limiting in development environments.
How It Works
Upgrade Flow
- The client creates a checkout session via
POST /billing/v1/checkoutwith success and cancel URLs. - The server creates a Stripe Checkout Session linked to the organization's Stripe customer (or creates a new customer).
- The Stripe-hosted checkout URL is returned. The client redirects the user to complete payment.
- After successful payment, Stripe sends a
checkout.session.completedwebhook to/billing/v1/webhooks/stripe. - The webhook handler verifies the
Stripe-Signatureheader againstSTRIPE_WEBHOOK_SECRET. - The handler links the Stripe customer ID and subscription ID to the organization record.
- The organization's tier is upgraded to
pro. Project rate limits and query timeouts increase immediately on the next request.
Webhook Event Processing
| Stripe Event | Action |
|---|---|
checkout.session.completed | Link Stripe customer and subscription to organization; upgrade tier to Pro |
customer.subscription.updated | Update tier based on the new plan (handles upgrades and downgrades) |
customer.subscription.deleted | Downgrade organization to Free tier |
Webhooks without a valid Stripe-Signature header are rejected with 401 Unauthorized. Webhooks with an invalid HMAC signature are rejected with 400 Bad Request.
Usage Tracking
Usage metrics are tracked per-project and aggregated over a rolling 30-day window. The gateway middleware increments counters for API calls on every request. Storage and bandwidth metrics are updated by the storage engine on upload and download operations. Function invocations are tracked by the functions runtime.
The GET /billing/v1/usage endpoint reads the aggregated metrics for the current 30-day period and returns them as a summary.
Materialized View Refresh
- An admin creates a view with a SQL query and a refresh interval (e.g.,
1h). - The materializer immediately executes the query against the tenant database and stores the result in the DashMap.
- A background task re-executes the query on the configured interval and updates the cached result.
- Read requests to
GET /analytics/v1/{view_name}serve directly from the DashMap, providing sub-millisecond latency. - For
manualrefresh views, the cache is only updated when the view is recreated.
GraphQL
Auto-generated GraphQL API from PostgreSQL introspection, with queries, mutations, subscriptions, DataLoader batching, RLS enforcement, and built-in GraphiQL IDE.
Model Context Protocol (MCP)
MCP server for AI assistants with auto-generated database tools, JSON-RPC 2.0 transport, SSE streaming, per-project configuration, and RLS enforcement.