Skip to content

Subscriptions and entitlements

An account has one subscription with one item per enrolled paid product: your account can be on Free for Flags, Standard for Logging, and Pro for Config at the same time. Products on Free are simply absent from the subscription — paid products show up as items. Each plan publishes a set of entitlement keys (numerical limits) that the platform enforces on writes. When you hit a limit, the API returns 402 Payment Required.

A concrete example

Your team is starting out. You sign up — every product begins on the implicit Free plan, and your account has no subscription yet. You stay on Free Flags forever (your usage fits within the Free limits) but find yourself wanting more managed loggers than Free Logging allows. You enroll Logging at Standard — the platform creates your subscription with one item for Logging. Three months later, configs grow — you add a Config item at Standard too. Flags stays implicit Free with no item on the subscription.

The same subscription now has two paid items (Logging and Config), each at its own plan. Both bill on the same monthly cycle.

Plans

Each product has four plans:

  • Free — the default. No card required. Generous-but-limited entitlements.
  • Standard — paid monthly. Bigger limits, suited to teams shipping to production.
  • Pro — paid monthly. Larger limits built for scale.
  • Enterprise — paid monthly. Largest limits, plus governance, security, and SSO controls for whole-org rollouts.

Plans are defined per product in the platform's catalog. The exact entitlement values live in the catalog and are surfaced via GET /api/v1/products (the public products endpoint, also powering the pricing page).

bash
curl https://app.smplkit.com/api/v1/products

Returns each product's plans, prices, limits, and marketing copy. Fully cacheable; no auth required.

Entitlement keys

Every plan publishes limits keyed by entitlement key. A value of -1 in any limit means unlimited. The keys with finite values today:

KeyMeaningBundled / FreeStandardProEnterprise
logging.managed_loggersLoggers with managed = true101001000unlimited
logging.groupsLog groups325100unlimited
config.itemsConfigs per account1050250unlimited
config.keysItems per config (per write)252502500unlimited
config.value_size_bytesPer-item value byte size1024102401024001048576
config.inheritance_depthMax parent chain depth2510unlimited
flags.itemsFlags with managed = true1050250unlimited
flags.rulesRules per flag (per write)525100unlimited
audit.included_events_per_monthCustomer-emitted audit events included per month1,000100,0001,000,00010,000,000
audit.retention_daysDays customer audit events are retained3036518253650
audit.siem_streamingSIEM streaming (forwarders) enabled0001
jobs.included_runs_per_monthScheduled job runs included per month3,000100,0001,000,00010,000,000

Metered. These keys are a monthly allotment, not a hard cap: the value above is included in the plan price, and usage beyond it can bill per unit at the plan's overage rate instead of failing. See Metered allotments and overage below.

Smpl Audit's "Bundled" tier ships free with any other smplkit product subscription — there's no separate audit subscription at that level. smplkit-emitted audit events (resource types prefixed smpl.) are unlimited at every tier and never count toward audit.included_events_per_month.

audit.siem_streaming is enforced at the fan-out path (POST /api/v1/events), not at the management surface. Forwarder configuration is plan-agnostic on every plan — you can create, list, update, retry, and delete forwarders regardless of subscription. When the entitlement is 0, the audit service short-circuits before any forwarder lookup: no HTTP delivery is attempted and no delivery records are written. See SIEM streaming for the full delivery-status semantics.

Other keys (config.environments, config.servers, config.users, flags.environments, flags.servers, flags.users, logging.environments, logging.servers, logging.users) are defined and may be enforced in the future, but ship -1 (unlimited) or 0 on every plan today. Authoritative values come from GET /api/v1/products.

How counting differs across products

Logging charges you only for managed loggers — auto-discovery is free, so a service that imports thousands of library loggers doesn't fill your quota. Logging groups count separately and are always managed.

Flags has the same managed/discovered split as Logging: only flags with managed = true (typically created via POST /api/v1/flags) count toward flags.items. Auto-discovery via POST /api/v1/flags/bulk is exempt from governance entirely, so a service that imports thousands of flag references doesn't fill your quota.

Config has no managed/discovered split; every config is explicit and counts toward config.items.

What happens at quota

When a write would push you past a limit, the platform returns:

http
HTTP/1.1 402 Payment Required
Content-Type: application/vnd.api+json
json
{
  "errors": [{
    "status": "402",
    "code": "entitlement_limit_reached",
    "title": "Subscription limit reached",
    "detail": "Your free plan allows a maximum of 15 managed loggers. Upgrade your subscription to increase this limit.",
    "meta": {
      "limit_key": "logging.managed_loggers",
      "current": 15,
      "maximum": 15,
      "plan": "free"
    }
  }]
}

The meta.limit_key tells you exactly which limit blocked you. Existing resources keep working — only the write that would exceed the limit fails. Reads, evaluations, and metric ingestion are unaffected.

The console surfaces these as inline upgrade prompts when you click an action that would fail.

Metered keys behave differently

The 402 above is the behavior for keys that cap hard. The metered keys (audit.included_events_per_month, jobs.included_runs_per_month — marked † in the table above) treat the plan limit as an included allotment rather than a wall: by default, usage beyond it keeps flowing and bills per unit. Whether a metered key 402s, bills, or stops at a budget depends on your account's overage policy — see the next section.

Metered allotments and overage

Most entitlement keys are hard caps — the write that would exceed them fails with a 402. Two keys instead use an allotment-plus-overage model, so that bursty, usage-based workloads aren't forced into a higher plan just to clear a one-off spike:

  • Smpl Auditaudit.included_events_per_month (customer-emitted audit events)
  • Smpl Jobsjobs.included_runs_per_month (scheduled job runs)

For these, the plan's entitlement value is the included allotment — the volume baked into the monthly plan price. Beyond the allotment, each additional unit bills at a small per-unit overage rate set by the plan, instead of the write hard-failing. You control whether overage is permitted at all, and can cap it, through account settings.

How the products endpoint describes it

GET /api/v1/products exposes two fields that, together, fully describe the metered model for each product:

  • metered_limits (on the product) — the list of limit keys on that product that are metered. Products with no metered limits return an empty list. Audit returns ["audit.included_events_per_month"]; Jobs returns ["jobs.included_runs_per_month"].
  • overage_rates (on each plan) — a map of metered limit key → per-unit overage price in micro-USD ($0.000001 each) charged for each unit beyond that plan's included allotment. A rate of 0 means the plan stops at its allotment with no overage available (Free plans are always 0). The field is omitted on plans of products that aren't metered.

The current schedule, straight from the catalog:

Overage rates are shown below in micro-USD, with the dollar equivalent per unit in parentheses:

PlanAudit — included events / overage per eventJobs — included runs / overage per run
Free1,000 / — (hard stop)3,000 / — (hard stop)
Standard100,000 / 50 micro-USD ($0.00005)100,000 / 2,000 micro-USD ($0.002)
Pro1,000,000 / 40 micro-USD ($0.00004)1,000,000 / 1,500 micro-USD ($0.0015)
Enterprise10,000,000 / 30 micro-USD ($0.00003)10,000,000 / 1,000 micro-USD ($0.001)

GET /api/v1/products is the authoritative source — read it rather than hard-coding these numbers, since allotments and rates can change.

Worked example

You're on Audit Standard (100,000 included events, 50 µ¢/event overage) and emit 150,000 events in a month. The first 100,000 are covered by your plan; the remaining 50,000 bill as overage: 50,000 × $0.00005 = $2.50 added to your invoice. Had you been on Free (overage rate 0), the 100,001st event would instead have been rejected with a 402.

Controlling overage: overage_policy and overage_budget_cents

Overage behavior is set per metered product in your account settings, independently of the plan you're on. Read and replace settings via GET / PUT /api/v1/accounts/current/settings (the PUT requires the OWNER or ADMIN role). The overage controls live in a sub-object keyed by the product (audit, jobs):

json
{
  "audit": { "overage_policy": "ALLOW", "overage_budget_cents": null },
  "jobs":  { "overage_policy": "CAPPED", "overage_budget_cents": 5000 }
}

overage_policy takes one of three values:

  • ALLOW (the default) — usage beyond the allotment is permitted and bills at the plan's per-unit overage rate. Requires a paid plan, a default payment method on file, and a non-zero overage rate; otherwise the key falls back to hard-stop behavior.
  • HARD_STOP — the metered key behaves like an ordinary hard cap: once the included allotment is consumed, further units are rejected with 402 Payment Required, exactly like the non-metered keys. No overage is ever billed.
  • CAPPED — overage is permitted, but only up to a monthly spend ceiling. Once accumulated overage cost would exceed overage_budget_cents, additional units are rejected with 402. A CAPPED policy requires a non-negative integer overage_budget_cents — setting CAPPED without one returns 400.

overage_budget_cents is the cap in cents (so 5000 = $50.00). It must be a non-negative integer or null. It is only meaningful under CAPPED; under ALLOW it is ignored, and under HARD_STOP no overage accrues at all.

These controls only apply to metered products. Setting overage_policy or overage_budget_cents under a non-metered product key (e.g. config) returns 400 so typos surface early. Products and keys default to ALLOW with no cap when the sub-object is absent.

Subscription statuses

Stripe-aligned. The status field on the subscription resource takes one of:

  • ACTIVE — the customary good state.
  • PAST_DUE — last invoice failed; Stripe is retrying. Functionality continues during the retry window.
  • CANCELED — subscription ended. Functionality continues until current_period_end, after which all items are dropped and the account reverts to implicit Free across the board.
  • null — fully discounted: an administrator has set discount_source = OVERRIDE at 100%, and Stripe is not involved. All paid items are honored without billing.

Until you enroll your first paid item the account has no subscription row at all — GET /api/v1/accounts/current/subscription returns 404. Free state is derived from the absence of a subscription, or from a subscription that simply has no item for a given product.

Subscription lifecycle

The whole subscription is one resource you replace with a PUT. Each call to PUT /api/v1/accounts/current/subscription sends the desired items list — the server diffs against your current state and reconciles. Mechanically, that produces five kinds of per-item transition:

  • New item — a product not currently on the subscription is added. Takes effect immediately; Stripe creates prorations for the remainder of the current period.
  • Upgrade — an existing item moves to a higher plan. Takes effect immediately; prorated charge today.
  • Downgrade — an existing item moves to a lower paid plan. Scheduled for current_period_end; the higher plan continues until then. The item shows pending_plan_change and scheduled_change_effective_at until the change takes effect.
  • Drop — a product currently on the subscription is omitted from the request. Scheduled for current_period_end; reverts to implicit Free then.
  • Unchanged — an item already at the requested plan is left as-is.

To reverse a pending downgrade or drop before period end, send another PUT with the item back at its current paid plan — the diff classifies it as UNCHANGED and clears the pending change. Cancellation of the whole subscription is just a PUT with an empty items list; if no paid items remain, Stripe schedules cancellation at period end.

Before committing a change you can call POST /api/v1/accounts/current/subscription/actions/preview with the same body to project per-item effects, prorated charges due today, and the next-invoice total without mutating anything.

The first paid item requires a payment method. Subsequent changes reuse the account's default payment method unless you specify another. See Manage subscriptions.

Cross-product platform limits

Some platform-wide capabilities are plan-resolved entitlements — they vary by the highest plan you hold across any paid product and return 402 Payment Required when exceeded. The rest are flat operational caps that are the same on every plan and return 409 Conflict.

Plan-resolved (platform.*)

The platform pseudo-product resolves at the highest plan held across your paid products (config/flags/logging/audit). If you're on Flags Pro and Logging Free, your platform.* entitlements come from the Pro tier. See GET /api/v1/products for the exact values per plan.

KeyWhat it gatesFreeStandardProEnterprise
platform.managed_environmentsEnvironments with managed = true (only managed environments count)2310unlimited
platform.writersAccount members whose role is OWNER, ADMIN, or MEMBER. Pending non-VIEWER invitations also count.1unlimitedunlimitedunlimited
platform.readersAccount members whose role is VIEWERunlimitedunlimitedunlimitedunlimited

Two new error codes carry context for the most common 402s and 400s on these surfaces:

  • environment_unmanaged (400) — a write to flags / config / logging tried to set a per-environment value on an unmanaged environment. Promote it first via PUT /api/v1/environments/{id} with managed: true.
  • environment_not_found (400) — the referenced environment doesn't exist for this account.

Flat operational caps (409 Conflict)

These remain the same on every plan; they exist to bound platform resource usage. Contact support if you legitimately need more.

  • 50 API keys per account
  • 50 context types per account
  • 10,000 context instances per account (includes services)
  • 100 pending invitations per account

Who can do what

ActionOWNERADMINMEMBERVIEWER
Read subscription, products
Read invoices, billing history
Replace subscription (add, upgrade, downgrade, drop items)
Manage payment methods