Volume-based tiered pricing in CGRates

63 views
Skip to first unread message

simba masamba

unread,
Sep 12, 2026, 6:16:35 AMSep 12
to CGRateS
Good Day Team,

In CGRates, is it possible to do volume-based tiered pricing where for instance we have a fixed rate for National calls say $0.60/min. 
We have customers who call a lot and we offer them different tiers based off the minutes they bring to us.
e.g.
0-1000mins use a rate of 60c/min
2000mins - 5K use 55c/min
5000mins - 10K mins use 50c/min
10K+ use 45c /min

My expectation is that I set this up, and at the end of the month or any time I can report on their usage and charge without having to calculate tiers manually at the end of the month
Method used in my env: Online Charging

How can I set this up such that I can make a few changes on rates depending on each customer and assign a profile specifically for them, then accurately bill them each month based off tier their in.

BR,

Armir Veliaj

unread,
Sep 15, 2026, 10:59:47 AMSep 15
to CGRateS
Hi Simba,

Yes, it is possible in CGRateS to do volume-based tiered pricing, and there is no need for manual recalculation at the end of the month.

You can create different balances on the account, and set the minutes together with a weight for each balance.

The idea is as in this sample:

Balance1 : 1000 minutes, Rate 0.60/min, Weight 10
Balance2 : 4000 minutes, Rate 0.55/min, Weight 9
Balance3 : 5000 minutes, Rate 0.50/min, Weight 8

CGRateS will consume the balances starting from the highest weight, so calls goes to Balance1 first at 0.60/min, and once it's consumed, it automatically moves to Balance2 at 0.55/min, then Balance3, all in real time, without you needing to track usage or switch rates manually.

You need to configure the tariffplans (RatingProfiles, RatingPlans, Actions...) based on your specific setup to be connected with those balances.

Hope this helps,

Thanks,
Armir

simba masamba

unread,
Sep 16, 2026, 2:57:39 PMSep 16
to cgr...@googlegroups.com
Hi Armir,

Many thanks for your response but I don't get the link between the balance and the rate.
I understand the weighted balance consumption. My question is how the 60c, 55c and 50c rates are linked to each balance. Does each balance reference a different RatingProfile/RatingPlan, or is there another mechanism to associate a specific rate with a specific balance? Also, is this approach implementing progressive tier pricing (first 1000 mins at 60c, next 4000 at 55c, etc.) or full-volume repricing based on the customer's total monthly usage?

BR,


--
You received this message because you are subscribed to the Google Groups "CGRateS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cgrates+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/cgrates/63046de3-01e9-4017-9e42-6b8c3eb715afn%40googlegroups.com.

Armir Veliaj

unread,
Sep 17, 2026, 12:02:12 PMSep 17
to CGRateS
Hi Simba,

On the balance, you set a RatingSubject, and the ratingSubject should exist in RatingProfiles.csv. Then you continue with setting up the other profiles for pricing (RatingPlans, with DestinationRates inside, and the Rates defined).
You may check the RALs section in the docs for a review: https://cgrates.readthedocs.io/en/latest/rals.html#rals

And for your last question, yes, this approach is progressive and applied in real time,as for sample with the first 1000 minutes at 60c, the next 4000 at 55c, and so on.

Hope this clarifies things for you.

Thanks,
Armir

simba masamba

unread,
Sep 17, 2026, 2:07:35 PMSep 17
to cgr...@googlegroups.com
Hi Armir,

I get it now conceptually.
Let me read through the docs, lab this out and advise with a working setup actively.
Thanks for your response.

BR

simba masamba

unread,
Sep 18, 2026, 9:42:41 AMSep 18
to CGRateS
hi Armir,

So just a follow up question,
Does this mean I create 1 rating profile and reference it across 3 rate plans?
or I create 3 rating profiles and reference them to 3 rate plans?

In all cases, the rating profile will have rating subject = account to be using this volume based tier?

BR,

Simba

Armir Veliaj

unread,
Sep 18, 2026, 11:05:58 AMSep 18
to CGRateS
Hi Simba,

You can have 3 entries inside RatingProfiles.csv, one per tier, each with a different Subject (e.g. tier1, tier2, tier3), and each pointing to its own RatingPlanId with its own rate, since you're going to have different prices per tier.

And for the creation of 3 balances, one per tier, the RatingSubject is the matching tier label.

So RatingSubject isn't necessarily "account" itself, it's a label per balance, referenced as Subject in RatingProfiles.csv to attach the right price.

Thanks,
Armir

simba masamba

unread,
Sep 22, 2026, 3:27:57 AMSep 22
to CGRateS
Good day Armir,

Many thanks to answering my many questions. I appreciate your time and effort.
I managed to lab this up - csvs attached. 

I setup my test number with 3 balances of varying weight
e.g.
SetBalance-Tier-RatingSubject.png

Initially as advised, I setup *voice balances of 3mins per balance but I was failing to make outbound calls.
I then switched the balances to *monetary and my calls starting working fine. 
Not sure what I am missing - may you have a look and advise?

BR,


Volume-Based-Pricing.txt

Armir Veliaj

unread,
Sep 23, 2026, 5:07:30 AMSep 23
to CGRateS
Hi Simba,

The reason your calls work with *monetary balances but not with *voice is most likely due to the unit value you used when setting up that balance. With *voice, if no unit is specified, the value is considered to be in nanoseconds. If you want to express it in minutes, it should be written as "3m", or as the raw value 180000000000 (3 minutes converted to nanoseconds), instead of "3mins".

You can send us the API call you used to configure the *voice balance so we can verify if that's the case.

Thanks,
Armir

simba masamba

unread,
Sep 25, 2026, 9:04:50 AMSep 25
to cgr...@googlegroups.com
hi Armir,

So that I am aware of nanoseconds and the "3m" syntax but still it did not work for me. I kept getting a "You do not have sufficient credit for this call" when attempting to dial a number that goes through on a *monetary balance.
API call:
armir-1.png

Then if I do a getAccount for that same number:
armir-2.png

So the *voice balance is clearly there but the call fails for a reason I can't see.

BR,

Simba


Armir Veliaj

unread,
Sep 25, 2026, 11:51:23 AMSep 25
to CGRateS
Hi Simba,

To reproduce your issue, could you please send us the following information:

-The CGRateS json configuration and the cgr-engine version you are using.
-Your latest tariff plans (Rates.csv, RatingProfiles.csv, RatingPlans.csv, DestinationRates.csv, etc.).
-The cgr-engine logs from the moment of the call, together with an ngrep capture:
sudo ngrep -t -W byline port 2012 or 2014 -d any
-The complete GetAccount API response for the account experiencing the issue.

Also make sure you have connections to *localhost enabled in your json configuration.


Thanks,
Armir

simba masamba

unread,
Sep 28, 2026, 4:10:45 AM (13 days ago) Sep 28
to cgr...@googlegroups.com
HI Armir,

my cgrates versions is v0.10.5 as per the below:
{
 "ActiveGoroutines": 32,
 "GoVersion": "go1.26.2",
 "MemoryUsage": "10.5MiB",
 "NodeID": "XXXXXXXXXXX",
 "RunningSince": "Fri Sep 25 08:16:32 UTC 2026",
 "Version": "CGR...@v0.10.5"

When I make the call and run tail -f /var/log/cgrates/CGRateS.log - I do not get any output

Using : ngrep -t -W byline port 2012 or 2014 -d any:
interface: any
filter: ( port 2012 or 2014 ) and (ip || ip6)
#####
T 2026/09/28 08:05:38.518438 10.170.56.112:41694 -> 10.170.56.131:2012 [AP] #5
{"method":"SessionSv1.InitiateSession","params":[{"GetAttributes":true,"AllocateResources":false,"InitSession":true,"ForceDuration":false,"ProcessThresholds":false,"ProcessStats":false,"AttributeIDs":null,"ThresholdIDs":null,"StatIDs":null,"Tenant":"voice.lab.liquidtelecom.net","ID":"0cfd2da","Time":"2026-09-28T08:05:38.516443748Z","Event":{"Account":"27110493433","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}],"id":9}

#
T 2026/09/28 08:05:38.518824 127.0.0.1:39958 -> 127.0.0.1:2012 [AP] #6
{"method":"AttributeSv1.ProcessEvent","params":[{"AttributeIDs":null,"Context":"*sessions","ProcessRuns":null,"Tenant":"voice.lab.liquidtelecom.net","ID":"0cfd2da","Time":"2026-09-28T08:05:38.516443748Z","Event":{"Account":"27110493433","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}],"id":24}

#
T 2026/09/28 08:05:38.519619 127.0.0.1:2012 -> 127.0.0.1:39958 [AP] #7
{"id":24,"result":null,"error":"NOT_FOUND"}

##
T 2026/09/28 08:05:38.519778 127.0.0.1:39958 -> 127.0.0.1:2012 [AP] #9
{"method":"ChargerSv1.ProcessEvent","params":[{"Tenant":"voice.lab.liquidtelecom.net","ID":"6c7bc20","Time":null,"Event":{"Account":"27110493433","CGRID":"4a283a1a05beb2ef1c56a13e19ef4c4a2ab7418d","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}],"id":25}

#
T 2026/09/28 08:05:38.520527 127.0.0.1:39958 -> 127.0.0.1:2012 [AP] #10
{"method":"AttributeSv1.ProcessEvent","params":[{"AttributeIDs":["ATTR_TOLLFREE_REVERSE_BILLING","ATTR_ALLOW_CALLS"],"Context":"*chargers","ProcessRuns":null,"Tenant":"voice.lab.liquidtelecom.net","ID":"6c7bc20","Time":null,"Event":{"Account":"27110493433","CGRID":"4a283a1a05beb2ef1c56a13e19ef4c4a2ab7418d","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","RunID":"*default","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}],"id":26}

##
T 2026/09/28 08:05:38.520726 127.0.0.1:2012 -> 127.0.0.1:39958 [AP] #12
{"id":26,"result":{"MatchedProfiles":["ATTR_ALLOW_CALLS"],"AlteredFields":["*req.AttrRun"],"CGREvent":{"Tenant":"voice.lab.liquidtelecom.net","ID":"6c7bc20","Time":null,"Event":{"Account":"27110493433","AttrRun":"default","CGRID":"4a283a1a05beb2ef1c56a13e19ef4c4a2ab7418d","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","RunID":"*default","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}},"error":null}

#
T 2026/09/28 08:05:38.520914 127.0.0.1:2012 -> 127.0.0.1:39958 [AP] #13
{"id":25,"result":[{"ChargerSProfile":"DEFAULT","AttributeSProfiles":["ATTR_ALLOW_CALLS"],"AlteredFields":["*req.RunID","*req.AttrRun"],"CGREvent":{"Tenant":"voice.lab.liquidtelecom.net","ID":"6c7bc20","Time":null,"Event":{"Account":"27110493433","AttrRun":"default","CGRID":"4a283a1a05beb2ef1c56a13e19ef4c4a2ab7418d","Category":"call","Destination":"27117909001","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","OriginHost":"10.170.248.140","OriginID":"0gQAAC8WAAACBAAALxYAAPrG8LHEiUSH...@10.170.248.200","RequestType":"*prepaid","RouteID":"voice.lab.liquidtelecom.net","RunID":"*default","SetupTime":"2026-09-28T08:05:38Z","Subject":"27110493433","SubscriberId":"27110493433","ToR":"*voice","Usage":"30s"}}}],"error":null}

##
T 2026/09/28 08:05:38.521131 127.0.0.1:39958 -> 127.0.0.1:2012 [AP] #15
{"method":"Responder.MaxDebit","params":[{"Category":"call","Tenant":"voice.lab.liquidtelecom.net","Subject":"27110493433","Account":"27110493433","Destination":"27117909001","TimeStart":"2026-09-28T08:05:38Z","TimeEnd":"2026-09-28T08:06:08Z","LoopIndex":0,"DurationIndex":30000000000,"FallbackSubject":"","RatingInfos":null,"Increments":null,"ToR":"*voice","ExtraFields":{"AttrRun":"default","DiameterIOI":"voice.lab.liquidtelecom.net","IMSChargingId":"db9c9ef19d39b2ec550e41052451210d","RouteID":"voice.lab.liquidtelecom.net","SubscriberId":"27110493433"},"MaxRate":0,"MaxRateUnit":0,"MaxCostSoFar":0,"CgrID":"4a283a1a05beb2ef1c56a13e19ef4c4a2ab7418d","RunID":"*default","ForceDuration":false,"PerformRounding":false,"DenyNegativeAccount":false}],"id":27}

#
T 2026/09/28 08:05:38.521625 127.0.0.1:2012 -> 127.0.0.1:39958 [AP] #16
{"id":27,"result":{"Category":"call","Tenant":"voice.lab.liquidtelecom.net","Subject":"27110493433","Account":"27110493433","Destination":"27117909001","ToR":"*voice","Cost":0,"Timespans":null,"RatedUsage":0,"AccountSummary":{"Tenant":"voice.lab.liquidtelecom.net","ID":"27110493433","BalanceSummaries":[{"UUID":"8233e4ed-2b05-439a-ac64-3f71dcb04caa","ID":"RAND_Balance-1","Type":"*voice","Value":300000000000,"Disabled":false},{"UUID":"4c4102f2-2ce5-4772-b0ad-8bf3f3d3f449","ID":"MAIN","Type":"*monetary","Value":0,"Disabled":false}],"AllowNegative":false,"Disabled":false}},"error":null}

#
T 2026/09/28 08:05:38.521761 10.170.56.131:2012 -> 10.170.56.112:41694 [AP] #17
{"id":9,"result":{"Attributes":null,"ResourceAllocation":null,"MaxUsage":0,"ThresholdIDs":null,"StatQueueIDs":null},"error":null}

##########
T 2026/09/28 08:05:53.257425 127.0.0.1:36968 -> 127.0.0.1:2012 [AP] #27
{"method":"CoreSv1.Status","params":[{"Tenant":"","APIKey":null,"RouteID":null}],"id":1}

##
T 2026/09/28 08:05:53.257769 127.0.0.1:2012 -> 127.0.0.1:36968 [AP] #29
{"id":1,"result":{"ActiveGoroutines":32,"GoVersion":"go1.26.2","MemoryUsage":"10.6MiB","NodeID":"liq-cgr-rals1-poc-jhb","RunningSince":"Fri Sep 25 08:16:32 UTC 2026","Version":"CGR...@v0.10.5"},"error":null}

############^Cexit
41 received, 12 matched

Lastly for the GetAccount API response:
{
    "id": 12,
    "result": {
        "ID": "voice.lab.liquidtelecom.net:27110493433",
        "BalanceMap": {
            "*monetary": [
                {
                    "Uuid": "4c4102f2-2ce5-4772-b0ad-8bf3f3d3f449",
                    "ID": "MAIN",
                    "Value": 0,
                    "ExpirationDate": "0001-01-01T00:00:00Z",
                    "Weight": 10,
                    "DestinationIDs": null,
                    "RatingSubject": "",
                    "Categories": null,
                    "SharedGroups": null,
                    "Timings": null,
                    "TimingIDs": null,
                    "Disabled": false,
                    "Factor": null,
                    "Blocker": false
                }
            ],
            "*voice": [
                {
                    "Uuid": "8233e4ed-2b05-439a-ac64-3f71dcb04caa",
                    "ID": "RAND_Balance-1",
                    "Value": 300000000000,
                    "ExpirationDate": "2026-09-30T23:59:59Z",
                    "Weight": 30,
                    "DestinationIDs": {
                        "*any": true
                    },
                    "RatingSubject": "Tier1",
                    "Categories": {
                        "call": true
                    },
                    "SharedGroups": null,
                    "Timings": null,
                    "TimingIDs": null,
                    "Disabled": false,
                    "Factor": null,
                    "Blocker": false
                }
            ]
        },
        "UnitCounters": null,
        "ActionTriggers": null,
        "AllowNegative": false,
        "Disabled": false,
        "UpdateTime": "2026-09-25T12:56:55.473148408Z"
    },
    "error": null
}

Best Regards,

Simba





cgrates json file.txt
poc_cgrates_za-main.zip

Armir Veliaj

unread,
Sep 30, 2026, 8:20:17 AM (11 days ago) Sep 30
to CGRateS
Hi Simba,

Your *voice balance has has "RatingSubject": "Tier1". That makes it a rated balance and the 30s are rated with the Tier1 tariff plans and the cost is taken from the MAIN monetary balance. MAIN is 0 and AllowNegative is false, so Responder.MaxDebit has no usage (Cost: 0, RatedUsage: 0), and SessionS then returns MaxUsage: 0 as capture by the ngrep.

If those minutes should be free, you can set the balance's RatingSubject to *zero1s. Then the voice balance is consumed by second increment at zero cost or *zero1m for minutes increment, and does not need any monetary balance. If you want them charged at Tier1, MAIN needs funds or AllowNegative has to be enabled.


Thanks,
Armir

simba masamba

unread,
Sep 30, 2026, 9:11:42 AM (11 days ago) Sep 30
to cgr...@googlegroups.com
Hi Armir,

Many thanks for your email.
My intent is that I can assign either *voice or *monetary balances and they are deducted at a cost, never free.
My question are:

1. So to ensure it draws from a *voice balance what should the "Rating Subject" be then?
2. What does the *zeros1s / *zero1m do in my case, I had set the Rating subject to match on tiers.
3. If I set "AllowNegative: true" , I have noticed that the account debits from the *default balance. Is this the correct behavior or can I get it to debit from the "MAIN" balance with a starting balance of "0" ?

Thanks.

BR,

Armir Veliaj

unread,
Sep 30, 2026, 11:02:54 AM (11 days ago) Sep 30
to CGRateS
Hi Simba,  



Please find inline the answers regarding your questions :

    1. So to ensure it draws from a *voice balance what should the "Rating Subject" be then?
   
A *voice balance is always consumed in time units, so the way to make a call draw only from it is a *zero rating subject, e.g. *zero1s. With any other subject (like your Tier1), the balance is treated as a rated balance, the seconds are taken from the voice balance and the cost calculated from the Tier1 tariff plan is also taken from a monetary balance inside the account.

    2. What does the *zeros1s / *zero1m do in my case, I had set the Rating subject to match on tiers.
   
They mark the voice balance as units consumed at zero cost, without needing a rating profile. The suffix is the increment: with *zero1s a 30s call takes 30s from the balance, with *zero1m it takes a full 60s. So "free" refers only to the monetary cost, the voice units are still deducted. As you mentioned that you do not want they get used as free units, you do not need to use *zero in RatingSubject.

   
   
    3. If I set "AllowNegative: true" , I have noticed that the account debits from the *default balance. Is this the correct behavior or can I get it to debit from the "MAIN" balance with a starting balance of "0" ?
   
Yes it is the correct behavior by design. The moment that you set"AllowNegative: true",CGRateS create a new balance named *default with initial credit of 0 and go negative on that one but the balance you created "MAIN" or any other custom balance remains at 0.


Thanks,
Armir

simba masamba

unread,
Sep 30, 2026, 3:16:35 PM (11 days ago) Sep 30
to cgr...@googlegroups.com
Hi Armir,

Many thanks for your response.
Let me lab this up and see the approach I would want to take in my setup.

BR,

Reply all
Reply to author
Forward
0 new messages