Logo

Grant

What is a Grant?

Grants determine how much usage a subject is allowed to consume, for example, the issueAfterReset property of metered entitlements also creates a grant in the background. The ability to directly interact with grants gives you immense flexibility in configuring and managing your metered entitlements. A grant is a singular record of you granting some amount of usage to a specific subject via a metered entitlement.

import { OpenMeter } from '@openmeter/sdk';
 
// Connect to locally running OpenMeter instance
const openmeter = new OpenMeter({
  baseUrl: 'http://127.0.0.1:8888',
});
 
// Grant 100 additional Tokens to customer-1
const grant = await openmeter.subjects.createEntitlementGrant(
  'customer-1',
  'gpt_4_tokens',
  {
    amount: 100,
    priority: 1,
    effectiveAt: new Date().toISOString(),
    expiration: {
      duration: 'MONTH',
      count: 1,
    },
  },
);

A metered entitlement works by running a balance calculation in which the feature usage is burnt down (deducted) from the grants issued for that entitlement. When issuing a grant you can define multiple behaviors for the grant that will affect how it behaves during that balance calculation.

Lets go through each of these concepts one by one.

Entitlement issue after reset

To automatically issue grants after the reset, set the issueAfterReset property in the entitlement.

EffectiveAt and Expiration

You can issue grants to be active in the past, present, or future (with the limit that it has to be later than the last reset time of the entitlement). The effectiveAt property of the grant determines when the grant becomes active, after that point the grants balance can be burnt down by the feature usage. You must define an expiration setting for the grant from which an expiresAt is calculated, after which no more usage can be burnt down from that grant. If a grant expires, any remaining balance it might have is lost.

Grant Expiration

Priority

Grants are burnt down in a deterministic order during balance calculation. Only grants that have a remaining balance can be burnt down, once a grant is fully consumed it is no longer considered for balance calculation. This order is reflected in burnDownHistory, a history segment ends either when the burn down order changes (grant fully consumed, or becoming active / inactive) or when the entitlement is reset. The burn down order is determined as follows:

  1. First, grants with higher priority are burnt down before grants with lower priority.
  2. In case of grants with the same priority, the grant that is closest to its expiration date is burnt down first.
  3. In case of grants with the same priority and expiration, the grant that was created first is burnt down first.

A lower number indicates a higher priority! Priority is a single byte integer, so the range is from 0 to 255 with 0 being the highest priority.

Grant Priority

Voiding Grants

Grants can be voided which has the same effect as if their expiresAt has been reached. Voiding a grant will immediately stop any further usage from being burnt down from that grant. The remaining balance of the grant is lost.

Rollover

Rollover is a special behavior determining what happens to grants at a reset. You have two properties to control rollover: minRolloverAmount and maxRolloverAmount. At a reset the grant's balance is updated using the following calculation:

Balance After Reset = MIN(Max Rollover Amount, MAX(Balance Before Reset, Min Rollover Amount))

Rollover lets you define how grant balance behaves accross resets, which provides two sets of capabilities: first, it lets you grant usage than can roll over accross resets, and second, you can issue grants that "top up" the balance after each reset. For example, if you wanted to issue additional 1000 usage from a one time purchase that can be used for a year, you can issue a grant with amount and maxRolloverAmount set to 1000 and expiration set to 1 year. Alternatively, if you wanted to set up a starting balance of 5000 based on the usage period, you could create a grant with amount, minRolloverAmount and maxRolloverAmount set to 5000, so after each reset the balance is topped up to 5000.

Recurrence

Recurrence is a special behavior that lets you define grants that top up their balance at a regular interval. The way this is different from configuring minRolloverAmount in the above example is that its independent of the usage period and resets. For example, if you've already set up the starting balance of 5000 in the above example, but want to grant an additional 300 usage each day, you can create a grant with amount set to 300 and recurrence set to 1 day.

Example Use Case

If a system is metered by token on usage, then as part of their subscription each customer gets 10.000 tokens/month. Certian users require more tokens than this, so we are granting them an additional 100.000 tokens/year for extra fees.

We would want the customer to first use their available balance from the 10.000 tokens/month allowed balance, and if they have used all of that, then they should start using the 100.000 tokens/year balance.

This can be achieved by creating two grants:

  • Grant 1: 10.000 tokens that rolls over each month with the usage period, priority=5
  • Grant 2: 100.000 tokens recurring each year, priority=10
const grant1 = await openmeter.subjects.createEntitlementGrant(
  'customer-1',
  'gpt_4_tokens',
  {
    amount: 10_000,
    priority: 5,
    effectiveAt: new Date().toISOString(),
    expiration: {
      duration: 'YEAR',
      count: 10,
    },
    minRolloverAmount: 10_000,
    maxRolloverAmount: 10_000,
  },
);
 
const grant2 = await openmeter.subjects.createEntitlementGrant(
  'customer-1',
  'gpt_4_tokens',
  {
    amount: 100_000,
    priority: 10,
    effectiveAt: new Date().toISOString(),
    expiration: {
      duration: 'YEAR',
      count: 10,
    },
    recurrence: {
      interval: 'YEAR',
      anchor: '2024-01-01T00:00:00.000Z',
    },
  },
);

Caveats

The entitlement engine stores historical usage data pre-aggregated in minute sized chunks. Due to this, events changing the entitlement balance (issuing grants, grants recurring, executing a reset...) cannot have sub-minute precision. This is achieved by simply flooring the relevant timestamps when executing the actions, so they can be then stored in history. This means that if you issue a grant with an effectiveAt of 2024-01-01T00:00:13Z, it will be rounded to 2024-01-01T00:00:00Z and the grant will be active from that point onwards. The same applies to expiration and recurrence settings, as well as reset effectiveAt.

This has some potentially unexpected consequences when using entitlements, here are some examples:

  1. You do a reset on an entitlement, some usage is registered, and then you want to do another reset all in the same minute. The second reset will return an error, as due to truncation it would register at the same time as the first one, which is not allowed (you can only reset after the last reset took place).
  2. You issue a grant with effectiveAt now and then do a reset with effectiveAt now, within the same minute. The reset and the grant register for the same time, so even though the grant was created before reset was called, the reset won't operate on that grant as it will be part of the next usage period, not the previous one.