Skip to main content

Trials and custom deals

Sales closes a deal: Acme stays on Starter, but gets SSO for a two-week trial and a much bigger API allowance. You don't need a new plan for that. Change what one customer has with gives, takes, and overrides:

ToolChangesEx.
GiveWhether a customer has an entitlement, using a plan's definition of it.SSO on Starter. Business's API limit for one org.
TakeDenies an entitlement the plan has, keeping its usage.Pause exports while an invoice is unpaid.
OverrideHow much: one customer's limit, mode, or reset.250,000 requests instead of 100,000.
import { Limitr } from '@formata/limitr';

const limitr = await Limitr.new(`
policy: {
credits: {
request: { label: 'API request' }
export: { label: 'Export' }
}
plans: {
starter: {
default: true
entitlements: {
api: {
limit: {
credit: 'request', value: 1_000
resets: true, reset_sch: 'monthly:1'
}
}
exports: { limit: { credit: 'export', value: 10, resets: true } }
}
}
business: {
entitlements: {
api: {
limit: {
credit: 'request', value: 100_000
resets: true, reset_sch: 'monthly:1'
}
}
exports: { limit: { credit: 'export', value: 500, resets: true } }
sso: {}
audit_log: {}
}
}
}
}`);

const DAY = 86_400_000;
await limitr.ensureCustomer('acme', 'starter', 'org', 'Acme');
await limitr.ensureCustomer('ada', '', 'user', 'Ada', ['acme']);
// Acme's features and API limit.
async function report(label: string) {
const features = await limitr.features('acme');
const apiLimit = await limitr.limit('acme', 'api');
console.log(`${label}:`, features, 'api limit', apiLimit);
}

await report('starter');

// 1. A trial of Business's SSO for 14 days, without changing Acme's plan.
await limitr.giveEntitlement('acme', 'sso', undefined, Date.now() + 14 * DAY);

// 2. A negotiated API allowance: Business's API limit for Acme only.
await limitr.giveEntitlement('acme', 'api', 'business');
await report('after the deal');
console.log('ada, on acme\'s plan:', await limitr.features('ada'));

// 3. Then a custom number on top: 250k requests, for this customer only.
await limitr.createCustomerOverride('acme', 'api', 250_000);
await report('with an override');

// 4. Pause exports while an invoice is unpaid. Usage so far is kept.
await limitr.takeEntitlement('acme', 'exports');
await report('exports paused');
await limitr.restoreEntitlement('acme', 'exports');

// 5. Gives and takes are stored on the customer, so they survive a plan change.
await limitr.setCustomerPlan('acme', 'business', false);
await limitr.setCustomerPlan('acme', 'starter', false);
await report('after an upgrade and a downgrade');
console.log('given to acme:', Object.keys((await limitr.customer('acme'))?.given ?? {}));

It prints:

starter: [ 'api', 'exports' ] api limit 1000
after the deal: [ 'api', 'exports', 'sso' ] api limit 100000
ada, on acme's plan: [ 'api', 'exports', 'sso' ]
with an override: [ 'api', 'exports', 'sso' ] api limit 250000
exports paused: [ 'api', 'sso' ] api limit 250000
after an upgrade and a downgrade: [ 'api', 'exports', 'sso' ] api limit 250000
given to acme: [ 'sso', 'api' ]

What happened​

  1. A trial. giveEntitlement('acme', 'sso', undefined, expiresOn) gave SSO from the closest plan that has it (Business), for 14 days. When it expires, SSO is gone again with no cleanup.
  2. A negotiated limit. giveEntitlement('acme', 'api', 'business') uses Business's definition of api, limit included. Nothing can be invented: a give always comes from a plan that defines the entitlement.
  3. A custom number. createCustomerOverride('acme', 'api', 250_000) changes the limit for Acme only, on top of the give.
  4. A pause. takeEntitlement denies exports even though the plan has them. The meter and any override stay, so restoreEntitlement picks up where it left off.
  5. Plan changes. Gives and takes are stored on the customer (given, taken), so they last through upgrades and downgrades. To keep a feature through a downgrade, give it before changing the plan.

Orgs and their users​

Ada has no plan of her own, so she's on Acme's, and she gets Acme's gives and takes with it. A give or take on Ada herself wins over Acme's.

Overrides belong to the customer they're set on. Acme's override changes Acme's limit, not Ada's. For a limit shared by the org, meter the entitlement on the org (scope: 'org'), and the org's override applies to every call.

Trials​

TrialHow
A higher plan's featuregiveEntitlement(id, 'sso', undefined, Date.now() + 14 * DAY)
More usage for a whilecreateCustomerOverride(id, 'chat', 1_000_000, Date.now() + 14 * DAY)
A whole plansetCustomerPlan(id, 'business', false) now, and back later; or a plan fee with a trial_period (Plan fees)

Keeping records​

Each change sends an event: customer-entitlement-given, -taken, -restored, plus customer-set. Store them and you have an audit trail of every deal. Gives, takes, and overrides are part of the customer record, so saving customers saves them.

See Customers for every option.