# Overview

Pricing management lets your team instantly update and experiment on pricing, without redeploying your app.

## Problem

Without pricing management:

* **Complex updates** — Changing pricing requires coordinated code changes across the frontend, backend, and marketing site. Rolling back changes is even harder.
* **Engineering bottlenecks** — Product and marketing depend on engineering for every pricing change.
* **Limited experimentation** — It's difficult to test and iterate, leading to suboptimal pricing and lost revenue.

## Solution

Define your pricing in Hypertune as flags instead of hardcoded values:

```graphql
type Root {
  pricing: Pricing!
}

type Pricing {
  stripePrices(plan: Plan!): [StripePrice!]!
  planContent(plan: Plan!): PlanContent!
  planFeatures: [PlanFeature!]!
  planOrdering: [Plan!]!
}

enum Plan { free, pro, enterprise }

type StripePrice {
  id: String!
  type: StripePriceType!
}

enum StripePriceType { flatFee, perSeat }

type PlanContent {
  name: String!
  description: String!
  features: [String!]!
}

type PlanFeature {
  name: String!
  value(plan: Plan!): PlanFeatureValue!
}

type PlanFeatureValue {
  isIncluded: Boolean!
  text: String!
}
```

Then reference it in your code:

{% code title="api/upgrade/route.ts" %}

```typescript
import { waitUntil } from '@vercel/functions'
import { NextResponse } from 'next/server'
import getHypertune from '@/lib/getHypertune'
import getStripe from '@/lib/getStripe'
import getTeamSize from '@/lib/getTeamSize'

export const runtime = 'edge'

export async function POST(request: Request) {
  const hypertune = await getHypertune({ isRouteHandler: true })

  const stripePrices = hypertune
    .pricing()
    .stripePrices({ args: { plan: 'pro' } })
    .map((price) => price.get())

  const teamSize = getTeamSize()

  const lineItems = stripePrices.map((price) => ({
    price: price.id,
    quantity: price.type === 'perSeat' ? teamSize : 1,
  }))

  const stripe = getStripe()

  const baseUrl = new URL(request.url).origin

  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    line_items: lineItems,
    success_url: `${baseUrl}/success`,
    cancel_url: `${baseUrl}/cancel`,
  })

  waitUntil(hypertune.flushLogs())

  return NextResponse.json({
    sessionUrl: session.url,
    sessionId: session.id,
  })
}
```

{% endcode %}

{% code title="components/Pricing.tsx" %}

```tsx
import FeaturesTable from './FeaturesTable'
import PlansTable from './PlansTable'
import getHypertune from '@/lib/getHypertune'

export default async function Pricing() {
  const hypertune = await getHypertune()
  const pricing = hypertune.pricing()

  return (
    <div className="pricing">
      <h2>Pricing</h2>
      <PlansTable pricing={pricing} />
      <FeaturesTable pricing={pricing} />
    </div>
  )
}
```

{% endcode %}

{% code title="components/PlansTable.tsx" %}

```tsx
import {
  type PricingNode,
  type PlanContentNode,
} from '@/generated/hypertune'

export default function PlansTable({
  pricing,
}: {
  pricing: PricingNode
}) {
  const planOrdering = pricing.planOrdering({
    itemFallback: 'free',
  })

  return (
    <div className="plans-table">
      {planOrdering.map((plan) => {
        const planContent = pricing.planContent({
          args: { plan },
        })
        return (
          <PlanColumn key={plan} planContent={planContent} />
        )
      })}
    </div>
  )
}

function PlanColumn({
  planContent,
}: {
  planContent: PlanContentNode
}) {
  const content = planContent.get()

  return (
    <div className="plan-column">
      <h3>{content.name}</h3>
      <p>{content.description}</p>
      <ul>
        {content.features.map((feature) => (
          <li key={feature}>{feature}</li>
        ))}
      </ul>
    </div>
  )
}
```

{% endcode %}

{% code title="components/FeaturesTable.tsx" %}

```tsx
import { type PricingNode } from '@/generated/hypertune'

export default function FeaturesTable({
  pricing,
}: {
  pricing: PricingNode
}) {
  const planOrdering = pricing.planOrdering({
    itemFallback: 'free',
  })
  const planFeatures = pricing.planFeatures()

  return (
    <table className="features-table">
      <thead>
        <tr>
          <th>Feature</th>
          {planOrdering.map((plan) => (
            <th key={plan}>{plan}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {planFeatures.map((feature) => {
          const featureName = feature.name({
            args: {},
            fallback: '',
          })
          return (
            <tr key={featureName}>
              <td>{featureName}</td>
              {planOrdering.map((plan) => {
                const value = feature
                  .value({ args: { plan } })
                  .get()
                return (
                  <td key={plan}>
                    {value.text ? (
                      <span className="text">{value.text}</span>
                    ) : value.isIncluded ? (
                      <span className="tick">✓</span>
                    ) : (
                      <span className="cross">✗</span>
                    )}
                  </td>
                )
              })}
            </tr>
          )
        })}
      </tbody>
    </table>
  )
}
```

{% endcode %}

This empowers product and marketing to instantly update and experiment on pricing from the Hypertune dashboard without any code changes or redeploys:

<div><figure><img src="/files/rB4otDT3u1XdD1rNlvQo" alt=""><figcaption></figcaption></figure> <figure><img src="/files/MqYVqzhUzb61EqmyZxIJ" alt=""><figcaption></figcaption></figure></div>

## Benefits

* **Instant updates** — Adjust pricing across your frontend, backend, and marketing site without redeploying or coordinating code changes.
* **Empowered teams** — Product and marketing can update pricing independently, without waiting on engineering.
* **Faster iteration** — Tune pricing in real time to maximize revenue and growth.

## ROI

These benefits help teams:

* Ship more pricing improvements, faster, with the same headcount.
* Improve key business metrics, e.g. revenue.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hypertune.com/pricing/overview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
