Pricing and Billing

Build any Pricing and Billing Model (Price Machine)

39min
amberflo price machine at the core of our billing cloud, built from the ground up to tackle the demands of real time usage based pricing and billing, is a highly flexible and scalable pricing machine state engine (a k a rating engine) we describe below some of the artifacts and how the pricing machine works this background will help you understand the unique and powerful capabilities and flexibility of the pricing engine and, we hope, will unleash your imagination in building and deploying the pricing models that best suit your products and customer base it provides the tools needed to create any pricing plan that you have in mind how the price machine works before delving into the different types of price machine nodes, we will provide a high level description for how the price calculation works amberflo invoice calculator is the logic which converts a specific meter's hourly usage aggregation for a given customer to an invoice the exact input which the invoice calculator consumes is the meter hourly usage aggregation of a given customer partitioned by all of the official dimensions of the invoice then the price calculator executes the following steps step 1 fetch the relevant price machine for the customer and the meter step 2 reduce the input to a smaller set of partitions as defined by the specific price machine step 3 execute the price machine on the reduce input the exact output of the price machine is set of what call an 'item variant invoice' an 'item variant invoice' is a partition of the invoice by a sub set of the meter official dimensions given these we will define each price machine using two properties required dimensions function example let's assume you have the following meter { "meterapiname" "api calls", "dimensions" \[ "region", "is urgent request" ], "metertype" "sum" } then the price machine will create the following 'item variant invoice' \[ { "variant" { "region" "us" }, "price" 33 5 }, // (10 + 67)/2 { "variant" { "region" "ca" }, "price" 8 5 } // (14 + 3)/2 ] the different types of price machines now that we know something about how amberflo's price machine works, we will introduce the different types of machines we offer price leaf node the leaf price machine is the simplest price machine one can think of, and yet as we will see next it by itself quite an advance price machine the properties of this machine are required dimensions none function tier based price step function bellow is the 'leaf price machine' schema type "object" description "a simple leaf model to calc the price of a single meter usage " properties allowpartialbatch type "boolean" description "default to false if true the leafnode machine won't round up the usage to the next whole unit of batch, but instead calc the price per the exact usage consumed " tiers type "array" description "this array represents the price step function of the given meter the price is calculated using the price tiers as described below, where each tier starts at startafterunit (included) and ends at the startafterunit of the next tier (excluded) " items type "object" properties batchsize type "integer" description "defines how much usage is included in a single batch when calculating the price the leaf node will round up the usage to next whole unit of batch and calc the price for the total amount of batches " priceperbatch type "number" description "this is your price for a single batch of usage in terms of the base currency unit of the account (usd in most cases) " startafterunit type "integer" required \ startafterunit \ batchsize \ priceperbatch type type "string" default "leafnode" required \ type \ tiers unless you want to partition the invoice by a subset of the meter dimensions then this machine is likely all you need let's look at a few examples of possible configuration for this machine example 1 1 a machine that calculates a price of $0 10 per 1 unit of usage for example the price of 12 units of usage is $1 20 { "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 1 } ], "allowpartialbatch" true } example 1 2 a machine that calculates a price of $0 50 per 5 units of usage with no partial batching unlike the previous example this machine doesn't allow partial batches, which means, the price usage will be rounded up to whole unit of the batch size, and the price will be determined based on the amount of batches for example, the price of 12 units of usage is $1 50 (3 batches) { "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 5, "priceperbatch" 0 5 } ], "allowpartialbatch" false } example 1 3 a machine that calculates a price of $0 10 per each 1 unit of usage for the first 10 units of usage, and then drop the price to $0 05 for each unit afterwards for example, the price of 12 units of usage is $1 10 (0 1 10 + 0 05 2) { "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 1 }, { "startafterunit" 10, "batchsize" 1, "priceperbatch" 0 05 } ], "allowpartialbatch" false } example 1 4 a machine that calculates a price of $0 per each 1 unit of usage for the first 10 units of usage, and then increases the price to $0 05 for each unit thereafter for example, the price of 12 units of usage is $0 1 (0 10 + 0 05 2) { "type" "leafnode", "tiers" \[ { "startafterunit" 10, "batchsize" 1, "priceperbatch" 0 05 } ], "allowpartialbatch" false } discrete price leaf node discrete price leaf node is another type of a leaf node it is different than the price leaf node in a way that it calculates the price of each usage time slot independently of other hours for example let's assume we have the following tiers 0 100 no charge 100 or more $1 per 1 unit of usage now let's assume we had the following usage day 1 95 day 2 75 with the original price leaf node we will charge that customer for $70 (95 + 75 100) this is because the that model applies the tiers for the usage over the entire period with this discrete price leaf node we will charge the customer for $0 because on each day we used less than 100 units the properties of this machine are required dimensions none function tier based price step function bellow is the 'discrete leaf price machine' schema \ type "object" properties allowpartialbatch type "boolean" tiers type "array" description "this array represents the price step function of the given meter the price is calculated using the price tiers as described below, where each tier starts at startafterunit (included) and ends at the startafterunit of the next tier (excluded) " items 	 type "object" properties batchsize type "integer" description "defines how much usage is included in a single batch when calculating the price the leaf node will round up the usage to next whole unit of batch and calc the price for the total amount of batches " priceperbatch type "number" description "this is your price for a single batch of usage in terms of the base currency unit of the account (usd in most cases) " startafterunit type "integer" required \ startafterunit \ batchsize \ priceperbatch type type "string" default "discreteleafnode" required \ type \ tiers volume based leaf node a leaf node can calc the price of a given usage (of a given invoice variant) without deferring any pricing decisions to subsequent nodes in other words a leaf node can't have subsequent nodes which it depends on for this leaf node the only thing that dictates the price of a meter is the total amount of usage over time this price node is based on the total usage volume for example, let's assume we have the following tiers from 0 > $1 per unit of usage from 10 > $3 per unit of usage example let's assume that during an invoice cycle we have a total of 15 units used then the price should be 15 3 = $45 the properties of this machine are required dimensions none function tier based price function (but not a step function) bellow is the 'volume based leaf leaf ' schema type "object" properties type type "string" default "volume based leaf node" volumetounitpricemap type "object" description "a map of tier to price" additionalproperties type "number" required \ type \ volumetounitpricemap example { "type" "volume based leaf node", "volumetounitpricemap" { "0 0" 0, "11 0" 10 } } limitations of the volume base price node in the example above we describe the following tiers table from 0 > $1 per unit of usage from 10 > $3 per unit of usage and to calculate the price of 15 units of usage we just multiple 15 by 3 = $45 but what if the values were decreasing as the usage goes ? for example let's assume we have the following table from 0 > $3 per unit of usage from 10 > $1 per unit of usage now, a customer with 15 unit of usage will pay $15, while a customer with 9 unit of usage will pay 9 3 = $27 that stands against the core ideas of "usage based pricing", that guide us to create fair price machines (where if you use more you pay more, and there is no reason to use the system more just to pay less) so, one by design limitation of this price machine is that the price per unit can only increase as we use the system more if you wish to let it decrease then please refer to the 'price leaf node' and 'discrete price lead node' mentioned above dimension matrix node this type of machine allows you to create a different price logic for different combinations of dimensions values basically you define a price leaf node for each combination of dimensions values notice that this price machine will drop any usage for which the dimension values are not defined in the price machine (we will share an example for this below) the properties of this machine are required dimensions as defined in the 'dimensionkeys' property function a sum of a tier based price step functions \ type "object" description "the dimensionmatrixnode allows us to define different meters for different combinations of dimensions values for example let's assume you want to bill your customers by the amount storage used over time also let's assume you have two types of storage hard disk and in memory, and you want to have a different price for each type of storage then add the storage type as a dimension of your meters, and have a different price leafnode for each sort of storage " properties dimensionkeys type "array" description "a list of dimensions which affect the price " items type "object" dimensionsprices type "array" items type "object" properties dimensionvalues type "array" description "a values one per each of the dimension keys the dimensionmatrixnode will partition the given usage by this dimensions (and drop any usage which doesn't have a corresponding dimensions values defined in this model) then the dimensionmatrixnode will calculate the price of each partitioned usage " items type "object" leafnode type "object" description "a simple leaf model which calc the price of a single meter usage " properties allowpartialbatch type "boolean" tiers type "array" items type "object" properties batchsize type "integer" priceperbatch type "number" startafterunit type "integer" required \ startafterunit \ batchsize \ priceperbatch type type "string" default "leafnode" required \ type \ tiers type type "string" default "dimensionmatrixnode" required \ type \ dimensionsprices \ dimensionkeys example 2 the machine below defines a price according to the following pricing plan equivalent price machine { "type" "dimensionmatrixnode", "dimensionkeys" \[ "region", "memory" ], "dimensionsprices" \[ { "dimensionvalues" \[ "us west 1", "1gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 001000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us west 1", "2gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 002000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us west 1", "4gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 002000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "1gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 001500000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "2gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 003000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "4gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 004500000 } ], "allowpartialbatch" false } } ] } distinct resource reducer this machine counts the distinct values for a subset of dimensions and then calculates a price using a price leaf node for the count the distinct values count function can be applied on each individual hour each day for the entire invoice period the properties of this machine are required dimensions as defined in the 'resourcedefiningdimensions' property function a sum of a tier based price step functions which is applied on the result of the distinct count \ type "object" description "use this price model if you want to to charge you customer based on a distinct count of some set of dimensions count for example, let's assume you have a dimension called job id, and you want to invoice your customers based on the amount of jobs they ran, then you can use this price model to accomplish that " properties granularity type "string" description "hourly, daily, entire invoice period" nextnode type "object" description "a simple leaf model which calc the price of a single meter usage " properties allowpartialbatch type "boolean" tiers type "array" items type "object" properties batchsize type "integer" priceperbatch type "number" startafterunit type "integer" required \ startafterunit \ batchsize \ priceperbatch type type "string" default "leafnode" required \ type \ tiers resourcedefiningdimensions type "array" description "the dimension names to use for the distinct count " items type "object" type type "string" default "distinct resource reducer" required \ type \ granularity \ nextnode \ resourcedefiningdimensions example 3 let's assume your company has a pool of machines and provides a service which allows your customers to use your machines to run all kind of tasks on demand let's also assume that you measure how long each task runs, but for simplicity you want to invoice customers by the distinct number of tasks that they run during the invoice period in such a case you can define the following price machine { "type" "distinct resource reducer", "resourcedefiningdimensions" \[ "country" ], "granularity" "entire invoice period", "nextnode" { "usagevariationsbytimemap" null, "dimensions" \["job id"], "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 5, "priceperbatch" 2 } ], "allowpartialbatch" false } } this machine will have a distinct count for all of the jobs that ran during the invoice period (regardless of how long they ran or if the runtime was spread over multiple hours or days), and charge the customer $2 per each batch of 5 jobs max reducer similarly to the 'distinct resource reducer' this price machine node can also reduce the time granularity of your hourly usage this price machine finds the max value to pick the value of each usage partition this model allows you to charge your customers based on the peak hourly usage over each day the entire invoice period the properties of this machine are required dimensions inherit the 'required dimensions' of the next node function apply the next node function over the hourly, daily or entire invoice max usage notice you can plug this price machine node to any of the price machines described above \ type "object" description "hours is the most granular time unit in amberflo amberflo calculates hourly aggregation of each of your meter, where the aggregations are partitioned according to the meter's official dimensions values in other words all of the price machines takes hourly meter aggregation partitioned by the dimension values you can use this node if you want to have your price model based on time unit which is less granular than a single hour for example if you want to have your price model based on the daily max value of any hourly aggregation of the same day, you can use this model to reduce the time granularity of the aggregations " properties granularity type "string" description "hourly, daily, entire invoice period" nextnode type "object" description "next node can be any sort price node " type type "string" default "max reducer" required \ granularity \ nextnode \ type example 4 1 let's use the previously mentioned example and assume your company has a pool of machines and provides a service that allows your customers to use your machines to run all kind of tasks but this time, let's assume you want to charge your customers according to the max hourly usage rate they incurred during the invoice period you can create a map reducer similar to that described below { "type" "max reducer", "granularity" "entire invoice period", "nextnode" { "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 5, "priceperbatch" 40 } ], "allowpartialbatch" false } } example 4 2 let's assume we take example 2, mentioned above (for the dimensions matrix), but this time we want to calculate based on the max daily value as mentioned, we can have any node as the next node of the max reducer price machine therefore we can just wrap the aforementioned 'dimensionmatrixnode' inside of a max reducer { "type" "max reducer", "granularity" "daily", "nextnode" { "type" "dimensionmatrixnode", "dimensionkeys" \[ "region", "memory" ], "dimensionsprices" \[ { "dimensionvalues" \[ "us west 1", "1gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 001000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us west 1", "2gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 002000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us west 1", "4gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 002000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "1gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 001500000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "2gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 003000000 } ], "allowpartialbatch" false } }, { "dimensionvalues" \[ "us east 2", "4gb" ], "leafnode" { "type" "priceperunitleafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 1, "priceperbatch" 0 004500000 } ], "allowpartialbatch" false } } ] } } avg usage reducer this node has a similar logic to the "max reducer" described above with two main differences it calculated the avg usage instead of a max although notice that similarly to the "max reducer" we can split the usage into hours (hourly avg), days (an avg value for each day of the invoice), or calc one avg value for the entire invoice period the avg is being calculated over the original time period of invoice regarding #2 avg is calculated using the following formula avg of usage from time t1 to time t2 = \[usage during t1 to t2] / \[t2 t1] now, let's look at the following example and ask ourselves what is the denominator, meaning what are the t2, and t1 values let’s assume today is 7/22/2022 we started an invoice at 7/15/2022 the end time of the invoice is 8/15/2022 to make things simple let's assume our avg price state machine granularity is 'entire invoice period' so, at 7/22 we have two options for how to calc the denominator option 1 from 7/15/2022 to 7/22/2022, using current usage option 2 from 7/15/2022 to 8/15/2022, using current usage when we said that the avg is being calculated over the original time period of invoice we meant is that we follow option 2 option 2, has some big advantages over option 1 among them are using option 2 the price only goes up and can't really fluctuate option 2 is a more fair pricing mechanism for scenarios where a customer changes it plan or offboards from a service \ type "object" description "hours is the most granular time unit in amberflo amberflo calculates hourly aggregation of each of your meter, where the aggregations are partitioned according to the meters official dimensions values in other words all of the price machines takes hourly meter aggregation partitioned by the dimension values you can use this node if you want to have your price model based on time unit which is less granular than a single hour for example if you want to have your price model based on the daily avg value of any hourly aggregation of the same day, you can use this model to reduce the time granularity of the aggregations " properties granularity type "string" description "hourly, daily, entire invoice period" nextnode type "object" description "next node can be any sort price node " type type "string" default "average reducer" required \ granularity \ nextnode \ type resource groups reducer the main reason to use this node is if you want to partition your usage by a certain set of dimension values which is not included in the next nodes, without specifying a unique leaf price machine as done in the dimensions matrix node for example, let's assume you operate on many regions but you have the same pricing logic for all regions having said that, let's assume that when generating the invoice you want to calculate the price per region using the same leaf node you can use this method to have such logic the properties of this machine are required dimensions a unified set as defined in 'resourcedefiningdimensions' + the 'required dimensions' of the next node function apply the next node function over the partitioned usage \ type "object" description "the main reason to use this node is if you want to partition your usage by a certain set of dimension values which isn't included in the next nodes for example, let's assume you operate on many regions but you have the same pricing logic for all regions having said that let's assume when coming up with the invoice you want to calculate the price per region using the same leaf node (let's say) you can use this method to have such logic " properties aggregationtype type "string" description "sum, max" nextnode type "object" description "use any price machine us next node " resourcedefiningdimensions type "array" description "the partitioning dimensions " items type "object" type type "string" default "resource groups reducer" required \ aggregationtype \ nextnode \ resourcedefiningdimensions \ type example 5 we can implement the example mentioned above (partition by region but keep the same price logic for all regions) using a configuration similar to the one below { "type" "resource groups reducer", "resourcedefiningdimensions" \[ "region" ], "nextnode" { "type" "leafnode", "tiers" \[ { "startafterunit" 0, "batchsize" 5, "priceperbatch" 0 1 } ], "allowpartialbatch" false }, "aggregationtype" "sum" } endpoints two ways to define a price machine via amberflo's console amberflo's console contains a wizard that allows you to define most of the models mentioned above it's an easy and safe way to do it if the ui doesn't support the exact configuration you want to have then you can always use our apis the url is http //app amberflo io/payments/pricing/amberflo/account pricing/product item price (just notice that the price machine is one attribute of the entire item price object create or update a product item price docid\ yobvbnyiqnq7hikkugfto )