# Guide

This guide builds on the SDK quickstart and shows you how to model, manage, and experiment on your pricing using Hypertune.

You'll learn how to:

* Create custom object types to model your pricing
* Create flags that use those custom object types
* Access those flags in your code
* Update your pricing
* Run experiments on your pricing

## Prerequisites

[Set up Hypertune](/getting-started/set-up-hypertune.md)

## Create custom object types to model your pricing

Go to the **Schema** view in the dashboard. Click the **+** button in the top-right of the sidebar. Select **Object**, enter a name, and click **Create**.

<figure><img src="/files/sIt7jnxE0RiUKinaF49e" alt=""><figcaption></figcaption></figure>

By default, the new object type has no fields.

Click **+ Add** to add a new field. Enter a name, select **List** from the Type dropdown, then select **New object type** from the Item type dropdown.

<figure><img src="/files/AyXRok7zfAf4XnRaUDlE" alt=""><figcaption></figcaption></figure>

Enter a name for the new type and click **Create**.

<figure><img src="/files/XSKIo3iICZ3VMM5XoLKA" alt=""><figcaption></figcaption></figure>

Repeat for each field you want to add. You can switch to the code view to make this easier. Then, click **Save**.

<figure><img src="/files/JvlpFL2QmyBZSKyCj3KR" alt=""><figcaption></figcaption></figure>

```graphql
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!
}
```

## Create flags for your pricing

Go to the **Flags** view in the dashboard. Click the **+** button in the top-right of the sidebar and select **Flag**.

<figure><img src="/files/4BvzOLfp74h8bp0UQ9tr" alt=""><figcaption></figcaption></figure>

Enter a name, set its type to the one you created earlier, and click **Create**.

<figure><img src="/files/Z90i614EzDkyB5vRr7kQ" alt=""><figcaption></figcaption></figure>

Select the nested flag for the **Stripe Prices**, open the options (⋯) menu on the List, and select **Insert enum switch**.

<figure><img src="/files/tE495Iissj6LKvcWrBAt" alt=""><figcaption></figcaption></figure>

Set the switch Control to the **Plan** argument.

<figure><img src="/files/dYt06if5RXGAVXaTRbtf" alt=""><figcaption></figcaption></figure>

You'll see a nested sidebar item for each plan. Add the Stripe Prices for each plan. These will be added as line items when creating a Stripe checkout session.

<figure><img src="/files/yY4jL3EBVg8wDAska5Pp" alt=""><figcaption></figcaption></figure>

Insert an enum switch on the **Plan Content** flag, and add content for each plan. This will be used to render the plans table that lets your users compare each plan.

<figure><img src="/files/WPeUtGLplCLdrtztQ1SC" alt=""><figcaption></figcaption></figure>

Select the **Plan Features** list flag. This will be used to render the detailed features table under the plans table.

Add a Plan Feature, enter a name for it, then select **Insert enum switch** on the value field.

<figure><img src="/files/MdVq6yvsGniYuwNGDeRx" alt=""><figcaption></figcaption></figure>

This lets you return different values for each plan.

<figure><img src="/files/9ifbcVMKreM6gG6mqeCM" alt=""><figcaption></figcaption></figure>

Repeat for all the features you want to show in your features table.

Finally, select the **Plan Ordering** flag, set the order that plans should appear in the plans table and features table, and click **Save**.

<figure><img src="/files/hSqyoKaOFOAwOZG9Smfq" alt=""><figcaption></figcaption></figure>

## Access flags to retrieve pricing

Regenerate the client:

{% tabs %}
{% tab title="npm" %}

```bash
npx hypertune
```

{% endtab %}

{% tab title="yarn" %}

```bash
yarn hypertune
```

{% endtab %}

{% tab title="pnpm" %}

```bash
pnpm hypertune
```

{% endtab %}
{% endtabs %}

Then use the generated methods for your flags to get the pricing configuration when creating Stripe checkout sessions and rendering pricing components:

{% 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 %}

## Update your pricing configuration

Go to the **Flags** view in the dashboard, expand the top-level flag with your pricing configuration in the left sidebar, and select the nested flag that you want to configure.

<figure><img src="/files/auUJ6qJ8vF39aaykQDAm" alt=""><figcaption></figcaption></figure>

Make your changes, then open the **Diff** view to review them. Click **Save**.

<figure><img src="/files/9ogWnU2lHr8uWWJiHNZ1" alt=""><figcaption></figcaption></figure>

## Add a new plan

Go to the **Schema** view in the dashboard, select the **Plan** enum, and add a value for the new plan.

<figure><img src="/files/lY2DFxX42wLVV1sVivZx" alt=""><figcaption></figcaption></figure>

Go to the **Flags** view, and enter the configuration for the new plan across each of your nested flags for the **Stripe Prices**, **Plan Content**, **Plan Features**, and **Plan Ordering**.

<figure><img src="/files/xwFS4os4w6EOdnMJ4FRC" alt=""><figcaption></figcaption></figure>

## A/B test a new plan

To measure the impact of the new plan on conversion and revenue, select the **Plan Ordering** flag.

<figure><img src="/files/CHXSxtxmqGsoJRWDUOzO" alt=""><figcaption></figcaption></figure>

Click **+ Experiment**, select **New experiment** from the dropdown, enter a name for it, click **Create**, then click **Insert**.

In the Test Variant, replace the old plan enum value with the new one you created earlier.

<figure><img src="/files/8eTJHFCvGPOiAwYf5OcN" alt=""><figcaption></figcaption></figure>

## Next steps

* Build an [impact analysis](/concepts/impact-analyses.md) or [funnel](/concepts/funnels.md) to view the performance of each experiment variant.
* Experiment on your plan content and plan features.


---

# 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/guide.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.
