Never spend another second building and maintaining pricing in your code

What you thought is going to be a one-off sprint became your day job

Building the right pricing takes too much of your time, leaving too little for your core product.

Solve complex pricing with entitlements

Entitlements set limits and govern how much value an account gets. They are the building blocks for a truly flexible, lightning-fast pricing & packaging infrastructure.

without stigg
with
const entitlement = await stigg.getMeteredEntitlement({
 customerId: user.orgId,
 featureId: "feature-active-boards",
 options: { requestedUsage: 1 }
});

if (entitlement.hasAccess) {
 // allow user to open another board...
 // finally, track that a new board was opened
 await stigg.reportUsage({ customerId: user.organizationId, featureId:
  "feature-active-boards", value: 1 });
}
const PRICING_PLANS = [
 {
   name: "Free",
   stripePriceId: "price_...",
   maxActiveBoards: 1,
   monthlyAutomationsLimit: 0,
 },
 {
   name: "Basic",
   stripePriceId: "price_...",
   maxActiveBoards: 5,
   monthlyAutomationsLimit: 10 // TODO: reset every month, we don't have a way to do this yet
 },
 {
   name: "Premium (New)",
   stripePriceId: "price_...",
   stripeYearlyPriceId: "price_...",
   maxActiveBoards: 50
 },
 {
   name: "Premium (Legacy)",
   stripePriceId: "price_...",
   stripeYearlyPriceId: "price_...",
   maxActiveBoards: 25
 }
 // ...
];

const subscriptions = await stripe.subscriptions.list({ customer: user.stripeCustomerId, status: "active" });
const activeSubscription = subscriptions.data[0];

if (!activeSubscription) {
 // todo: customer has no active subscription, should we treat this as free?
}

const planStripePriceId = activeSubscription.items.data[0].price.id;
const plan = PRICING_PLANS.find(plan => plan.stripePriceId === planStripePriceId || plan.stripeYearlyPriceId === planStripePriceId);

if(!plan) {
 // todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
}

const org = await Organization.findOne({ id: user.orgId });

const boardsCount = await Board.count({ organizationId: org.id, status: "ACTIVE" });

if (boardsCount < plan.boardsLimit || boardsCount < org.legacyEnterpriseBoardsLimit || org.isVIP) {
 // finally allow user to open another board
}
with
without stigg
const entitlement = await stigg.getMeteredEntitlement({
 customerId: user.orgId,
 featureId: "feature-active-boards",
 options: { requestedUsage: 1 }
});

if (entitlement.hasAccess) {
 // allow user to open another board...
 // finally, track that a new board was opened
 await stigg.reportUsage({ customerId: user.organizationId, featureId: "feature-active-boards", value: 1 });
}
const PRICING_PLANS = [
 {
   name: "Free",
   stripePriceId: "price_...",
   maxActiveBoards: 1,
   monthlyAutomationsLimit: 0,
 },
 {
   name: "Basic",
   stripePriceId: "price_...",
   maxActiveBoards: 5,
   monthlyAutomationsLimit: 10 // TODO: reset every month, we don't have a way to do this yet
 },
 {
   name: "Premium (New)",
   stripePriceId: "price_...",
   stripeYearlyPriceId: "price_...",
   maxActiveBoards: 50
 },
 {
   name: "Premium (Legacy)",
   stripePriceId: "price_...",
   stripeYearlyPriceId: "price_...",
   maxActiveBoards: 25
 }
 // ...
];

const subscriptions = await stripe.subscriptions.list({ customer: user.stripeCustomerId, status: "active" });
const activeSubscription = subscriptions.data[0];

if (!activeSubscription) {
 // todo: customer has no active subscription, should we treat this as free?
}

const planStripePriceId = activeSubscription.items.data[0].price.id;
const plan = PRICING_PLANS.find(plan => plan.stripePriceId === planStripePriceId || plan.stripeYearlyPriceId === planStripePriceId);

if(!plan) {
 // todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
}

const org = await Organization.findOne({ id: user.orgId });

const boardsCount = await Board.count({ organizationId: org.id, status: "ACTIVE" });

if (boardsCount < plan.boardsLimit || boardsCount < org.legacyEnterpriseBoardsLimit || org.isVIP) {
 // finally allow user to open another board
}
entitlement = stigg.get_metered_entitlement(
   customer_id=user["orgId"],
   feature_id="feature-active-boards",
   options={"requested_usage": 1}
)

if entitlement["has_access"]:
    # allow user to open another board...
    # finally, track that a new board was opened
    stigg.report_usage(customer_id=user["organizationId"],
    feature_id="feature active-boards", value=1)
PRICING_PLANS = [
   {
       "name": "Free",
       "stripePriceId": "price_...",
       "maxActiveBoards": 1,
       "monthlyAutomationsLimit": 0,
   },
   {
       "name": "Basic",
       "stripePriceId": "price_...",
       "maxActiveBoards": 5,
       "monthlyAutomationsLimit": 10 # TODO: reset every month, we don't have a way to do this yet
   },
   {
       "name": "Premium (New)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 50
   },
   {
       "name": "Premium (Legacy)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 25
   }
   # ...
]

subscriptions = stripe.subscriptions.list(customer=user["stripeCustomerId"], status="active")
active_subscription = subscriptions["data"][0]

if not active_subscription:
   # todo: customer has no active subscription, should we treat this as free?
   pass

plan_stripe_price_id = active_subscription["items"]["data"][0]["price"]["id"]
plan = next((plan for plan in PRICING_PLANS if plan["stripePriceId"] == plan_stripe_price_id or plan["stripeYearlyPriceId"] == plan_stripe_price_id), None)

if not plan:
   # todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
   pass

org = Organization.find_one(id=user["orgId"])

boards_count = Board.count(organizationId=org["id"], status="ACTIVE")

if boards_count < plan["boardsLimit"] or boards_count < org["legacyEnterpriseBoardsLimit"] or org["isVIP"]:
   # finally allow user to open another board
   pass
with
without stigg
entitlement = stigg.get_metered_entitlement(
   customer_id=user["orgId"],
   feature_id="feature-active-boards",
   options={"requested_usage": 1}
)

if entitlement["has_access"]:
    # allow user to open another board...
    # finally, track that a new board was opened
    stigg.report_usage(customer_id=user["organizationId"], feature_id="feature active-boards", value=1)
PRICING_PLANS = [
   {
       "name": "Free",
       "stripePriceId": "price_...",
       "maxActiveBoards": 1,
       "monthlyAutomationsLimit": 0,
   },
   {
       "name": "Basic",
       "stripePriceId": "price_...",
       "maxActiveBoards": 5,
       "monthlyAutomationsLimit": 10 # TODO: reset every month, we don't have a way to do this yet
   },
   {
       "name": "Premium (New)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 50
   },
   {
       "name": "Premium (Legacy)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 25
   }
   # ...
]

subscriptions = stripe.subscriptions.list(customer=user["stripeCustomerId"], status="active")
active_subscription = subscriptions["data"][0]

if not active_subscription:
   # todo: customer has no active subscription, should we treat this as free?
   pass

plan_stripe_price_id = active_subscription["items"]["data"][0]["price"]["id"]
plan = next((plan for plan in PRICING_PLANS if plan["stripePriceId"] == plan_stripe_price_id or plan["stripeYearlyPriceId"] == plan_stripe_price_id), None)

if not plan:
   # todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
   pass

org = Organization.find_one(id=user["orgId"])

boards_count = Board.count(organizationId=org["id"], status="ACTIVE")

if boards_count < plan["boardsLimit"] or boards_count < org["legacyEnterpriseBoardsLimit"] or org["isVIP"]:
   # finally allow user to open another board
   pass
entitlement = Stigg.get_metered_entitlement(
  customer_id: user[:org_id],
  feature_id: "feature-active-boards",
  options: {requested_usage: 1}
)

if entitlement[:has_access]
 # allow user to open another board...
 # finally, track that a new board was opened
 Stigg.report_usage(
   customer_id: user[:organization_id],
   feature_id: "feature-active-boards",
   value: 1
 )
end
PRICING_PLANS = [
   {
       name: "Free",
       stripePriceId: "price_...",
       maxActiveBoards: 1,
       monthlyAutomationsLimit: 0
   },
   {
       name: "Basic",
       stripePriceId: "price_...",
       maxActiveBoards: 5,
       monthlyAutomationsLimit: 10 # TODO: reset every month, we don't have a way to do this yet
   },
   {
       name: "Premium (New)",
       stripePriceId: "price_...",
       stripeYearlyPriceId: "price_...",
       maxActiveBoards: 50
   },
   {
       name: "Premium (Legacy)",
       stripePriceId: "price_...",
       stripeYearlyPriceId: "price_...",
       maxActiveBoards: 25
   }
   # ...
]

subscriptions = Stripe::Subscription.list(customer: user[:stripe_customer_id], status: "active")
active_subscription = subscriptions.data[0]

if !active_subscription
 # todo: customer has no active subscription, should we treat this as free?
end

plan_stripe_price_id = active_subscription.items.data[0].price.id
plan = PRICING_PLANS.find {|plan| plan[:stripePriceId] == plan_stripe_price_id || plan[:stripeYearlyPriceId] == plan_stripe_price_id}if !plan
 # todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
end

org = Organization.find_by(id: user[:org_id])

boards_count = Board.where(organization_id: org.id, status: "ACTIVE").count

if boards_count < plan[:boardsLimit] || boards_count < org[:legacyEnterpriseBoardsLimit] || org[:isVIP]
 # finally allow user to open another board
end
with
without stigg
entitlement = Stigg.get_metered_entitlement(
  customer_id: user[:org_id],
  feature_id: "feature-active-boards",
  options: {requested_usage: 1}
)

if entitlement[:has_access]
 # allow user to open another board...
 # finally, track that a new board was opened
 Stigg.report_usage(
   customer_id: user[:organization_id],
   feature_id: "feature-active-boards",
   value: 1
 )
end
PRICING_PLANS = [
   {
       name: "Free",
       stripePriceId: "price_...",
       maxActiveBoards: 1,
       monthlyAutomationsLimit: 0
   },
   {
       name: "Basic",
       stripePriceId: "price_...",
       maxActiveBoards: 5,
       monthlyAutomationsLimit: 10 # TODO: reset every month, we don't have a way to do this yet
   },
   {
       name: "Premium (New)",
       stripePriceId: "price_...",
       stripeYearlyPriceId: "price_...",
       maxActiveBoards: 50
   },
   {
       name: "Premium (Legacy)",
       stripePriceId: "price_...",
       stripeYearlyPriceId: "price_...",
       maxActiveBoards: 25
   }
   # ...
]

subscriptions = Stripe::Subscription.list(customer: user[:stripe_customer_id], status: "active")
active_subscription = subscriptions.data[0]

if !active_subscription
 # todo: customer has no active subscription, should we treat this as free?
end

plan_stripe_price_id = active_subscription.items.data[0].price.id
plan = PRICING_PLANS.find {|plan| plan[:stripePriceId] == plan_stripe_price_id || plan[:stripeYearlyPriceId] == plan_stripe_price_id}if !plan
 # todo: customer has an active subscription but we don't know what plan it is ??? maybe some older legacy plan?
end

org = Organization.find_by(id: user[:org_id])

boards_count = Board.where(organization_id: org.id, status: "ACTIVE").count

if boards_count < plan[:boardsLimit] || boards_count < org[:legacyEnterpriseBoardsLimit] || org[:isVIP]
 # finally allow user to open another board
end
entitlement, _ := stigg.GetMeteredEntitlement(map[string]interface{}{
  "customerId": user["org_id"],
  "featureId": "feature-active-boards",
  "options": map[string]interface{}{
     "requestedUsage": 1
  }
})

if entitlement["hasAccess"] {
   // allow user to open another board...
   // finally, track that a new board was opened

   stigg.ReportUsage(map[string]interface{}{
       "customerId": user["organizationId"],
       "featureId": "feature-active-boards",
       "value": 1
    })
}
package main

import (
   "fmt"
   "github.com/stripe/stripe-go"
   "github.com/stripe/stripe-go/subscription"
)

var PRICING_PLANS = []map[string]interface{}{
   {
       "name": "Free",
       "stripePriceId": "price_...",
       "maxActiveBoards": 1,
       "monthlyAutomationsLimit": 0,
   },
   {
       "name": "Basic",
       "stripePriceId": "price_...",
       "maxActiveBoards": 5,
       "monthlyAutomationsLimit": 10, //TODO: reset every month, we don't have a way to do this yet
   },
   {
       "name": "Premium (New)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 50,
   },
   {
       "name": "Premium (Legacy)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 25,
   },
   //...
}

params := &stripe.SubscriptionListParams{
   Customer: user["stripe_customer_id"],
   Status: "active",
}

subscriptions, _ := subscription.List(params)
active_subscription := subscriptions.Data[0]

if active_subscription == nil {
   //todo: customer has no active subscription, should we treat this as free?
}

plan_stripe_price_id := active_subscription.Items.Data[0].Price.ID
plan := getPlan(plan_stripe_price_id)if plan == nil {
   //todo: customer has an active subscription but we don't know what plan it is??? maybe some older legacy plan?
}

org, _ := Organization.FindOne(map[string]interface{}{"id": user["org_id"]})

boards_count, _ := Board.Count(map[string]interface{}{"organizationId": org.ID, "status": "ACTIVE"})

if boards_count < plan["boardsLimit"] || boards_count < org["legacyEnterpriseBoardsLimit"] || org["isVIP"] {
   //finally allow user to open another board
}

func getPlan(planStripePriceId string) map[string]interface{} {
   for _, plan := range PRICING_PLANS {
       if plan["stripePriceId"] == planStripePriceId || plan["stripeYearlyPriceId"] == planStripePriceId {
           return plan
       }
   }
   return nil
}
with
without stigg
entitlement, _ := stigg.GetMeteredEntitlement(map[string]interface{}{
  "customerId": user["org_id"],
  "featureId": "feature-active-boards",
  "options": map[string]interface{}{
     "requestedUsage": 1
  }
})

if entitlement["hasAccess"] {
   // allow user to open another board...
   // finally, track that a new board was opened

   stigg.ReportUsage(map[string]interface{}{
       "customerId": user["organizationId"],
       "featureId": "feature-active-boards",
       "value": 1
    })
}
package mainimport (
   "fmt"
   "github.com/stripe/stripe-go"
   "github.com/stripe/stripe-go/subscription"
)

var PRICING_PLANS = []map[string]interface{}{
   {
       "name": "Free",
       "stripePriceId": "price_...",
       "maxActiveBoards": 1,
       "monthlyAutomationsLimit": 0,
   },
   {
       "name": "Basic",
       "stripePriceId": "price_...",
       "maxActiveBoards": 5,
       "monthlyAutomationsLimit": 10, //TODO: reset every month, we don't have a way to do this yet
   },
   {
       "name": "Premium (New)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 50,
   },
   {
       "name": "Premium (Legacy)",
       "stripePriceId": "price_...",
       "stripeYearlyPriceId": "price_...",
       "maxActiveBoards": 25,
   },
   //...
}

params := &stripe.SubscriptionListParams{
   Customer: user["stripe_customer_id"],
   Status: "active",
}

subscriptions, _ := subscription.List(params)
active_subscription := subscriptions.Data[0]

if active_subscription == nil {
   //todo: customer has no active subscription, should we treat this as free?
}

plan_stripe_price_id := active_subscription.Items.Data[0].Price.ID
plan := getPlan(plan_stripe_price_id)if plan == nil {
   //todo: customer has an active subscription but we don't know what plan it is??? maybe some older legacy plan?
}

org, _ := Organization.FindOne(map[string]interface{}{"id": user["org_id"]})

boards_count, _ := Board.Count(map[string]interface{}{"organizationId": org.ID, "status": "ACTIVE"})

if boards_count < plan["boardsLimit"] || boards_count < org["legacyEnterpriseBoardsLimit"] || org["isVIP"] {
   //finally allow user to open another board
}

func getPlan(planStripePriceId string) map[string]interface{} {
   for _, plan := range PRICING_PLANS {
       if plan["stripePriceId"] == planStripePriceId || plan["stripeYearlyPriceId"] == planStripePriceId {
           return plan
       }
   }
   return nil
}

Entitlements give engineers full control over pricing & packaging

Authentication

Solves user identity

“Can the user prove who they claim be?”

Authorization

Solves access restriction

“Can the user access that feature based on his role?”

Feature flagging

Solves release management

“Did we release that feature to that segment of users under that environment?”

Entitlements

Solves access gating

“Is that account subscribed to access that feature?”

“What becomes increasingly clear to me is that companies need a monetization platform. If you build it as a platform instead of single entities, you’ll be in a much better place going forward.”  Check out Snyk’s story
Ben Grohbiel's profile image
Ben Grohbiel
Senior Engineering Manager

What’s holding you back?

Plan versioning

Safely roll out changes any way you want: Grandfather plans or segment by attributes, meta data, location, & more.

Multi-environment support

Create as many environments as you want, like development, staging,  production. Each one is fully segregated.

Native billing integration

Integrate with Stripe in one click, switch billing providers just as easily. Oh, and we take care of the maintenance, too.

Webhook automation

Send webhook events from Stigg to trigger actions in any application, like user notifications at the end of a trial.

Plug-and-play widgets

Easily introduce self-service components, like a paywall or a customer portal, out of the box.

Secure & scalable

Stigg is SOC2 complaint, built for reliability & availability, and runs on first-class monitoring systems.

Stigg vs alternatives

Features
Stripe
Feature Flags
Built in-house
Provisioning & access control
V sign
X sign
Limited
Info sign
Feature flags are intended to control the rollout of features, and therefore will determine whether a customer can see the feature, not how it’s monetized. Stigg also gives you the full upsell experience out-of-the-box, such as paywalls, upgrade/downgrade behavior, and more.
Burning poop's gif
Usage metering
V sign
Limited
Info sign
Metering is limited to the metric that’s used for billing. To regularly update usage, Stripe users will have to implement an aggregation mechanism on top of Stripe’s solution. Metered features won’t automatically reset every defined period, so you'll have to build it yourself.
X sign
Burning poop's gif
arrow to down
Self-service widgets
V sign
Limited
X sign
Burning poop's gif
V sign
V sign
X sign
Burning poop's gif
V sign
V sign
X sign
Burning poop's gif
Plan versioning / gradual roll-out
V sign
X sign
X sign
Burning poop's gif
Trial support
Time-based, reverse & quota
Limited
Info sign
Stripe only supports time-based trials. With Stigg, you can also limit trials by quota and implement reverse trials (downgrade users to a free plan when the trial ends).
X sign
Burning poop's gif
Enterprise plan support
V sign
X sign
V sign
Burning poop's gif
Multi-environment support
V sign
V sign
V sign
Burning poop's gif
A/B testing & experiments
V sign
X sign
Depends on implementation
Info sign
Stigg offers a holistic experimentation capability out-of-the-box which covers pricing, packaging and control over customer journey. While it’s possible to implement similar functionality using feature flags, covering all of these use-cases takes a considerable amount of time to build.
Burning poop's gif
In-app usage analytics
V sign
X sign
X sign
Burning poop's gif
Integrations
V sign
V sign
V sign
Burning poop's gif
Stripe
Feature Flags
Built in-house
Provisioning & access control
V sign
V sign
Limited
Info sign
Feature flags are intended to control the rollout of features, and therefore will determine whether a customer can see the feature, not how it’s monetized. Stigg also gives you the full upsell experience out-of-the-box, such as paywalls, upgrade / downgrade behavior, and more.
X sign
Burning poop's gif
Usage metering
V sign
Limited
Info sign
Metering is limited to the metric that’s used for billing. To regularly update usage, Stripe users will have to implement an aggregation mechanism on top of Stripe’s solution. Metered features won’t automatically reset every defined period, so you'll have to build it yourself.
X sign
X sign
Burning poop's gif
V sign
Limited
X sign
Burning poop's gif
V sign
X sign
X sign
Burning poop's gif
V sign
V sign
X sign
Burning poop's gif
V sign
V sign
X sign
Burning poop's gif
V sign
V sign
X sign
Burning poop's gif
Coming soon
X sign
X sign
Burning poop's gif
Plan versioning / gradual roll-out
V sign
X sign
X sign
Burning poop's gif
Trial support
Time-based, reverse & quota
Limited
Info sign
Stripe only supports time-based trials. With Stigg, you can also limit trials by quota and implement reverse trials (downgrade users to a free plan when the trial ends).
X sign
X sign
Burning poop's gif
Enterprise plan support
V sign
X sign
V sign
Burning poop's gif
Multi-environment support
V sign
V sign
V sign
Burning poop's gif
A/B testing & experiments
V sign
X sign
Depends on implementation
Info sign
Burning poop's gif
In-app usage analytics
V sign
X sign
X sign
Burning poop's gif
Integrations
V sign
V sign
V sign
Burning poop's gif
“We had a first cut of the platform in about 3 weeks.
I thought “Oh yeah, we’re almost done!”. And I’d say I’m quite seasoned.
However, it took 3–4 more months of full-time efforts by several engineers to get to what we have today.”  Check out Reclaim’s story
Patrick Lightbody's profile image
Patrick Lightbody,
Cofounder

Integrate in 4 steps

1
Model your pricing in Stigg
2
Embed Stigg’s snap-in widgets
3
Report usage & set limits
4
Connect to billing & CRM
Fight spaghetti code. Launch flexible pricing that scales.
Start now

Fight spaghetti code. Launch flexible pricing that scales.

Try Stigg