# 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](https://docs.hypertune.com/getting-started/set-up-hypertune)

## 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2Ff7g8HTQ6nZnTrRNB80Gk%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%20(2).png?alt=media&#x26;token=3186a140-947c-4d43-84ce-3740bee57d82" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FLo0HRQeFYQrMveDumQWQ%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%20(3).png?alt=media&#x26;token=f4cc26c2-ce2d-4f34-ae16-cb912b6b1073" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2F67tnrnn8B8ybCeGl5rwi%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%20(4).png?alt=media&#x26;token=2a53f415-3591-4f52-9332-de944c38d0e6" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FIEn7ouQoPFXotcsbNzuh%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(6).png?alt=media&#x26;token=d033196d-4267-4f47-955b-cbd25d8e295d" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2Fq8pQr3FwV3CEPdagrF6H%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(2).png?alt=media&#x26;token=d7cbbc56-f5e4-4724-a495-cf3b753818ec" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FpkJfLqOpIVQaRpfzylQZ%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(3).png?alt=media&#x26;token=5b6354a5-5606-4dee-915b-cac8d4e07b2c" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2F3xI0rgOGWYXIDJ2ZiEyp%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(4).png?alt=media&#x26;token=9a213987-3077-4241-8d49-ec6f4f7c5a5b" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FgV4TZbBRNFTcI4f8QYXI%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(5).png?alt=media&#x26;token=149ad99a-b20a-44b0-b31f-9a8a21939c4e" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2F0pHUQ6tX43xsa1fEr04t%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(8).png?alt=media&#x26;token=d76e2dd3-0f77-45bb-a94c-b930e9afdfa0" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FiuRoKOQHb9UeAoFlfweb%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(7).png?alt=media&#x26;token=fb98b74d-b7e1-41d3-b78c-342791689039" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FJA4j6wTFQUjH6DRKwzwV%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(11).png?alt=media&#x26;token=2f558c59-d7da-41e8-b4e5-81e401d41592" alt=""><figcaption></figcaption></figure>

This lets you return different values for each plan.

<figure><img src="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FDT264R9nlShzwbnX9gd3%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(12).png?alt=media&#x26;token=2506405e-12d5-4e6a-910b-df42914b834c" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FLZDmseqiFO2baMUCSGFY%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(13).png?alt=media&#x26;token=569dbc54-bdf1-4566-bafd-f9c94e8da261" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FgRotR2PFC7OqgN2rA7Lv%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(18).png?alt=media&#x26;token=4e72b4b7-d087-4128-8890-9b4abc93d6a7" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FgGnmO8w2zaozQJagnS7x%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(17).png?alt=media&#x26;token=ff9998aa-315a-4cbd-9c3f-75755aed2240" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FtQJjUHatiH0iYAYBpw2M%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(19).png?alt=media&#x26;token=e3350512-62a7-4a11-8c85-7c983d3d9ff3" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FMTH0TS16f8tE4BoAMcAE%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(8).png?alt=media&#x26;token=41cf315e-6c3f-46da-8daa-bb7dff48a748" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FU7aihtlkQVrA3Ye3FXnA%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(9).png?alt=media&#x26;token=f52485a7-9d97-46aa-a048-64840e993ee6" 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="https://2048905609-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWa3rQLiu4JZhBRkiyoKz%2Fuploads%2FrjppzHXbNUNEfSQMPvo2%2Flocalhost_3000_projects_6715_main_draft_schema_setup%3D0%26selected_schema_type%3D%257B%2522type%2522%253A%2522object%2522%252C%2522name%2522%253A%2522StripePrice%2522%252C%2522selectedChildName%2522%253Anull%252C%2522count%2522%253A1%257D%26schema_mode%3Dcode%20(10).png?alt=media&#x26;token=7e75caf5-4704-4510-8383-2cca2d498945" alt=""><figcaption></figcaption></figure>

## Next steps

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