Cloud Quick Start
Get started with Limitr Cloud.
Limitr Cloud connects the enforcement engine you already understand to a managed layer that handles policy versioning, customer state, live per-customer margin, usage-based invoicing, and team alerting. The integration is a one-line change. Everything else stays the same.
A Limitr Cloud account and an API token from the dashboard. Familiarity with the Quick Start is assumed but not required.
Connect
import { Limitr } from '@formata/limitr';
const policy = await Limitr.cloud({ token: process.env.LIMITR_TOKEN });
That's the swap. Your policy now lives in the Cloud dashboard — versioned, auditable, editable by any authorized team member without a deploy. allow(), increment(), check(), remaining() — every method works identically to the local engine.
Limitr.cloud() returns undefined if the token is invalid or the connection fails. Always check for it:
const policy = await Limitr.cloud({ token: process.env.LIMITR_TOKEN });
if (!policy) throw new Error('Failed to connect to Limitr Cloud — check your token');
What changes
Policy sync. The WebSocket connection fetches your policy from the dashboard on startup and keeps it live. When you update a plan limit, add a tier, or change an overage rule in the dashboard, your running application reflects it immediately — no deploy, no restart.
Customer state sync. The first time any enforcement method is called for a customer ID that doesn't exist locally, the SDK fetches their state from Cloud before proceeding. For new customers, use ensureCustomer() — it's a no-op if the customer already exists locally or in Cloud.
Enforcement stays in-process. The WebSocket carries policy updates and customer state — it is not on the hot path. allow() is still sub-millisecond.
What you get
Policy management without deploys
Every change made in the Cloud dashboard takes effect in your running application within seconds. Changes are versioned and auditable — you can see who changed what and when, and roll back immediately if something goes wrong.
To pin to a specific policy version rather than always receiving the latest (rarely recommended):
const policy = await Limitr.cloud({
token: process.env.LIMITR_TOKEN,
policy: 'pol_abc123', // specific version ID from the dashboard
});
// omit 'policy' (or use 'active') to always receive the latest published version
Live per-customer margin
Because your credits carry overhead_cost and price, Limitr Cloud surfaces cost-to-serve versus captured revenue per customer in real time. Open the dashboard and see which customers are margin-positive, which are margin-negative, and which are about to exhaust a soft limit — before the billing period closes.
Usage-based invoicing
Invoices are generated directly from metered consumption. What customers used is what they owe. Cloud connects to your billing provider and handles invoice generation from the policy's metered state.
Team alerting
Notification conditions defined in your policy route automatically to Slack or email. A customer crossing 80% of their limit, a hard limit firing on a key account, a soft limit going into overage — define the condition once in the policy, and Cloud routes it to the right team without additional application code.
denyUnconnected
When the WebSocket connection drops — network hiccup, Cloud maintenance, deployment restart — denyUnconnected controls what allow() returns for customers whose state isn't locally available.
const policy = await Limitr.cloud({
token: process.env.LIMITR_TOKEN,
denyUnconnected: true, // default
});
| Value | Behavior |
|---|---|
true (default) | allow() returns false when disconnected. Conservative — you never over-serve. |
false | allow() works normally when disconnected; remote sync is queued and flushed on reconnect. |
For most applications, when disconnected from Cloud there are larger issues than just Limitr. The default (true) is recommended unless your product is intentionally offline-capable or runs in environments without a consistent connection.
The reconnect is automatic. Once the connection is restored, customer state re-syncs and enforcement resumes normally.
Graceful shutdown
close() flushes any pending state to Cloud before closing the WebSocket. Call it during your application's shutdown sequence.
process.on('SIGTERM', async () => {
await policy.close();
process.exit(0);
});
Without close(), any metered usage that hasn't been flushed will be sent on the next reconnect. In most cases this is fine — the SDK queues unsent data. close() makes the flush synchronous and immediate.
Full initialization options
const policy = await Limitr.cloud({
token: string, // Required. API token from the dashboard.
policy?: string, // Policy version ID. Default: 'active' (latest published).
connectTimeout?: number, // ms to wait for initial connection. Default: 5000.
denyUnconnected?: boolean, // Deny allow() when disconnected. Default: true.
validate?: boolean, // Validate the policy on load. Default: true.
wsAddress?: string, // Override WebSocket endpoint. Default: wss://api.limitr.dev.
ticketAddress?: string, // Override auth ticket endpoint. Default: https://api.limitr.dev.
});
wsAddress and ticketAddress are for self-hosted or private Cloud deployments. Most users should never set these.
From local to Cloud
If you've followed the Quick Start, the transition is:
// Before
import { Limitr } from '@formata/limitr';
import { readFileSync } from 'fs';
const policy = await Limitr.new(readFileSync('./policy.yaml', 'utf-8'), 'yaml');
// After — remove the file, remove the format arg, add the token
const policy = await Limitr.cloud({ token: process.env.LIMITR_TOKEN });
if (!policy) throw new Error('Cloud connection failed');
// Everything below this line is identical
await policy.ensureCustomer('user_abc', 'starter');
const allowed = await policy.allow('user_abc', 'chat_input', 4200);
const remaining = await policy.remaining('user_abc', 'chat_input');
Your policy file is no longer needed in your repository. Your plan limits, credit definitions, and overage rules now live in the dashboard and can be updated by anyone with access — without touching your codebase.