SaaS & API Monetization

You sell access to APIs. Customers pay for tiers. Usage must be tracked, enforced, and auditable.

What decisions matter here?

Every API call is a decision:

These decisions happen at the gateway, thousands of times per second. Each one affects billing, compliance, and customer experience.

What goes wrong today?

Scattered enforcement Rate limiting in one service, quota tracking in another, entitlement checks in a third. When a customer disputes a bill, you reconstruct events from three systems.

Version drift Customer is on v1.0. You ship v2.0 with breaking changes. Their integration breaks. They didn’t ask to upgrade—it just happened.

Quota overruns Customer exceeds their plan limits. You don’t notice until the invoice goes out. Now it’s a billing dispute, not a technical conversation.

Audit gaps Regulator asks: “Show me every access decision for this customer in Q3.” You export logs from four systems and hope they’re complete.

What Hexarch changes structurally

Version-Locked Entitlements

From the AccessPlan and Subscription data model:

interface Subscription {
  id: string;
  appId: string;
  apiId: string;
  apiVersionId: string;       // Locked to this API version
  planId: string;             // Locked to this plan
  status: 'Pending Approval' | 'Active' | 'Suspended' | 'Revoked';
  gatewayAcknowledge?: boolean; // Optional: last known gateway acknowledgement
}

When a customer subscribes, they’re bound to a specific version. The gateway enforces this binding. Breaking changes in v2.0 don’t affect v1.0 subscribers.

Before: “We shipped v2, hope nobody breaks” After: “v1 subscribers stay on v1 until they explicitly upgrade”

Tiered Access Plans

From the AccessPlan interface:

interface AccessPlan {
  id: string;
  apiVersionId: string; // Explicit binding to a specific version
  quota: { value: number; unit: 'Requests' | 'Bytes'; period: 'Second' | 'Minute' | 'Hour' | 'Day' | 'Month' };
  rateLimit: { requestsPerSecond: number; burst: number };
  billingModel: 'Free' | 'Usage-Based' | 'Subscription';
  approvalRequired: boolean;
}

Plans are enforced at the gateway, not in application code. Free tier gets 100/day at 1 req/s. Business gets 10,000/day at 50 req/s. The policy engine tracks and enforces—your services just receive authorized requests.

Before: Rate limiting scattered across services After: Centralized enforcement, one source of truth for plan limits

Auditable Decisions

From the policy decision stream (GET /policy-decisions/stream), typically consumed via guardrailsStreamPolicyDecisions():

interface PolicyDecision {
  id: string;
  at: string;
  decision: 'ALLOW' | 'DENY';
  reason: string;
  policies: string[];
}

Every authorization decision is recorded with the policy that made it. When a customer asks “why was I denied?”, you show them the exact policy, the exact timestamp, the exact reason.

Before: “Let me check the logs… probably rate limiting?” After: “Request denied at 14:32:01 by policy rate-limit-free-tier, reason: exceeded 1 req/s burst”

Gateway Propagation

From the GatewayNode interface:

interface GatewayNode {
  appliedSnapshotId: string;
  desiredSnapshotId: string;
  status: 'Ready' | 'Starting' | 'Error' | 'Divergent';
}

When you change a plan, the change propagates as a new configuration snapshot. You can observe rollout and drift (applied vs desired) across the gateway fleet.

Before: “Config deployed, should be active now” After: “Config applied to 12/12 nodes, verified via hash comparison”

The Hexarch Difference

ConcernTraditionalHexarch
Rate limitingPer-service implementationGateway policy, centralized
Version bindingHope and prayerImmutable subscription locks
Quota trackingBilling system reconciliationReal-time enforcement at gateway
Audit trailLog aggregation from N servicesSingle stream of decision events
Plan changesRedeploy servicesPolicy update, no code change

Try It