---
title: Admin
description: Contains functions for authenticating and interacting with the Admin API.
api_version: v2
source_url:
  html: 'https://shopify.dev/docs/api/shopify-app-remix/v2/authenticate/admin'
  md: 'https://shopify.dev/docs/api/shopify-app-remix/v2/authenticate/admin.md'
api_name: shopify-app-remix
---

# Admin

Contains functions for authenticating and interacting with the Admin API.

This function can handle requests for apps embedded in the Admin, Admin extensions, or non-embedded apps.

## authenticate.​admin(**[request](#authenticateadmin-propertydetail-request)**​)

Authenticates requests coming from the Shopify admin.

The shape of the returned object changes depending on the `isEmbeddedApp` config.

### Parameters

* **request**

  **Request**

  **required**

### Returns

* **Promise\<AdminContext\<Config, Resources>>**

### AdminContext

```ts
Config['isEmbeddedApp'] extends false
  ? NonEmbeddedAdminContext<Config, Resources>
  : EmbeddedAdminContext<Config, Resources>
```

### NonEmbeddedAdminContext

* admin

  Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.

  ```ts
  AdminApiContext<Resources>
  ```

* billing

  Billing methods for this store, based on the plans defined in the \`billing\` config option.

  ```ts
  BillingContext<Config>
  ```

* cors

  A function that ensures the CORS headers are set correctly for the response.

  ```ts
  EnsureCORSFunction
  ```

* session

  The session for the user who made the request. This comes from the session storage which \`shopifyApp\` uses to store sessions in your database of choice. Use this to get shop or user-specific data.

  ```ts
  Session
  ```

### AdminApiContext

Provides utilities that apps can use to make requests to the Admin API.

* graphql

  Methods for interacting with the Shopify Admin GraphQL API

  ```ts
  GraphQLClient<AdminOperations>
  ```

* rest

  Methods for interacting with the Shopify Admin REST API There are methods for interacting with individual REST resources. You can also make \`GET\`, \`POST\`, \`PUT\` and \`DELETE\` requests should the REST resources not meet your needs.

  ```ts
  RestClientWithResources<Resources>
  ```

### GraphQLClient

* query

  ```ts
  Operation extends keyof Operations
  ```

* options

  ```ts
  GraphQLQueryOptions<Operation, Operations>
  ```

returns

```ts
interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}, interface Promise<T> {}, Promise: PromiseConstructor, interface Promise<T> {
    readonly [Symbol.toStringTag]: string;
}, interface Promise<T> {
    /**
     * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
     * resolved value cannot be modified from the callback.
     * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
     * @returns A Promise for the completion of the callback.
     */
    finally(onfinally?: (() => void) | undefined | null): Promise<T>;
}
```

### GraphQLQueryOptions

* apiVersion

  The version of the API to use for the request.

  ```ts
  ApiVersion
  ```

* headers

  Additional headers to include in the request.

  ```ts
  Record<string, any>
  ```

* tries

  The total number of times to try the request if it fails.

  ```ts
  number
  ```

* variables

  The variables to pass to the operation.

  ```ts
  ApiClientRequestOptions<Operation, Operations>
  ```

### RestClientWithResources

```ts
RemixRestClient & {resources: Resources}
```

### RemixRestClient

* session

  ```ts
  Session
  ```

* get

  Performs a GET request on the given path.

  ```ts
  (params: GetRequestParams) => Promise<Response>
  ```

* post

  Performs a POST request on the given path.

  ```ts
  (params: PostRequestParams) => Promise<Response>
  ```

* put

  Performs a PUT request on the given path.

  ```ts
  (params: PostRequestParams) => Promise<Response>
  ```

* delete

  Performs a DELETE request on the given path.

  ```ts
  (params: GetRequestParams) => Promise<Response>
  ```

### BillingContext

Provides utilities that apps can use to request billing for the app using the Admin API.

* cancel

  Cancels an ongoing subscription, given its ID.

  ```ts
  (options: CancelBillingOptions) => Promise<AppSubscription>
  ```

* check

  Checks if the shop has an active payment for any plan defined in the \`billing\` config option.

  ```ts
  (options: CheckBillingOptions<Config>) => Promise<BillingCheckResponseObject>
  ```

* request

  Requests payment for the plan.

  ```ts
  (options: RequestBillingOptions<Config>) => Promise<never>
  ```

* require

  Checks if the shop has an active payment for any plan defined in the \`billing\` config option.

  ```ts
  (options: RequireBillingOptions<Config>) => Promise<BillingCheckResponseObject>
  ```

### CancelBillingOptions

* isTest

  ```ts
  boolean
  ```

* prorate

  Whether to prorate the cancellation.

  ```ts
  boolean
  ```

* subscriptionId

  The ID of the subscription to cancel.

  ```ts
  string
  ```

### CheckBillingOptions

* isTest

  Whether to include charges that were created on test mode. Test shops and demo shops cannot be charged.

  ```ts
  boolean
  ```

* plans

  The plans to check for. Must be one of the values defined in the \`billing\` config option.

  ```ts
  (keyof Config["billing"])[]
  ```

### RequestBillingOptions

* amount

  Amount to charge for this plan.

  ```ts
  number
  ```

* currencyCode

  Currency code for this plan.

  ```ts
  string
  ```

* interval

  Interval for this plan. Must be set to \`OneTime\`.

  ```ts
  BillingInterval.OneTime
  ```

* isTest

  Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.

  ```ts
  boolean
  ```

* lineItems

  The line items for this plan.

  ```ts
  ({ interval?: BillingInterval.Every30Days | BillingInterval.Annual; discount?: { durationLimitInIntervals?: number; value?: { amount?: number; percentage?: never; } | { amount?: never; percentage?: number; }; }; amount?: number; currencyCode?: string; } | { interval?: BillingInterval.Usage; amount?: number; terms?: string; currencyCode?: string; })[]
  ```

* plan

  The plan to request. Must be one of the values defined in the \`billing\` config option.

  ```ts
  keyof Config["billing"]
  ```

* replacementBehavior

  The replacement behavior to use for this plan.

  ```ts
  BillingReplacementBehavior
  ```

* returnUrl

  The URL to return to after the merchant approves the payment.

  ```ts
  string
  ```

* trialDays

  How many trial days to give before charging for this plan.

  ```ts
  number
  ```

### RequireBillingOptions

* isTest

  Whether to include charges that were created on test mode. Test shops and demo shops cannot be charged.

  ```ts
  boolean
  ```

* onFailure

  How to handle the request if the shop doesn't have an active payment for any plan.

  ```ts
  (error: any) => Promise<Response>
  ```

* plans

  The plans to check for. Must be one of the values defined in the \`billing\` config option.

  ```ts
  (keyof Config["billing"])[]
  ```

### EnsureCORSFunction



### EmbeddedAdminContext

* admin

  Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.

  ```ts
  AdminApiContext<Resources>
  ```

* billing

  Billing methods for this store, based on the plans defined in the \`billing\` config option.

  ```ts
  BillingContext<Config>
  ```

* cors

  A function that ensures the CORS headers are set correctly for the response.

  ```ts
  EnsureCORSFunction
  ```

* redirect

  A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded apps. Returned only if \`isEmbeddedApp\` is \`true\`.

  ```ts
  RedirectFunction
  ```

* session

  The session for the user who made the request. This comes from the session storage which \`shopifyApp\` uses to store sessions in your database of choice. Use this to get shop or user-specific data.

  ```ts
  Session
  ```

* sessionToken

  The decoded and validated session token for the request. Returned only if \`isEmbeddedApp\` is \`true\`.

  ```ts
  JwtPayload
  ```

### RedirectFunction

* url

  ```ts
  string
  ```

* init

  ```ts
  RedirectInit
  ```

returns

```ts
TypedResponse<never>
```

### RedirectInit

```ts
number | (ResponseInit & {target?: RedirectTarget})
```

### RedirectTarget

```ts
'_self' | '_parent' | '_top'
```

Examples

### Examples

* ####

  ##### Description

  Authenticate, run API mutation, and redirect

  ##### /app/routes/\*\*.ts

  ```ts
  import {type ActionFunctionArgs, json} from '@remix-run/node';
  import {GraphqlQueryError} from '@shopify/shopify-api';

  import {authenticate} from '../shopify.server';

  export const action = async ({request}: ActionFunctionArgs) => {
    const {admin, redirect} = await authenticate.admin(request);

    try {
      await admin.graphql(
        `#graphql
        mutation updateProductTitle($input: ProductInput!) {
          productUpdate(input: $input) {
            product {
              id
            }
          }
        }`,
        {
          variables: {
            input: {id: '123', title: 'New title'},
          },
        },
      );

      return redirect('/app/product-updated');
    } catch (error) {
      if (error instanceof GraphqlQueryError) {
        return json({errors: error.body?.errors}, {status: 500});
      }

      return new Response('Failed to update product title', {status: 500});
    }
  };
  ```

* ####

  ##### Description

  Use the \`cors\` helper to ensure your app can respond to requests from admin extensions.

  ##### /app/routes/admin/my-route.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { session, cors } = await authenticate.admin(request);
    return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));
  };
  ```

* ####

  ##### Description

  Use the \`redirect\` helper to safely redirect between pages.

  ##### /app/routes/admin/my-route.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { session, redirect } = await authenticate.admin(request);
    return redirect("/");
  };
  ```

* ####

  ##### Description

  Pass in a \`target\` option of \`\_top\` or \`\_parent\` to go to an external URL.

  ##### /app/routes/admin/my-route.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { session, redirect } = await authenticate.admin(request);
    return redirect("/", { target: '_parent' });
  };
  ```

* #### Using offline sessions

  ##### Description

  Get your app's shop-specific data using an offline session.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { session } = await authenticate.admin(request);
    return json(await getMyAppData({shop: session.shop));
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";

  const shopify = shopifyApp({
    // ...etc
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Using online sessions

  ##### Description

  Get your app's user-specific data using an online session.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { session } = await authenticate.admin(request);
    return json(await getMyAppData({user: session.onlineAccessInfo!.id}));
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";

  const shopify = shopifyApp({
    // ...etc
    useOnlineTokens: true,
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Using the decoded session token

  ##### Description

  Get user-specific data using the \`sessionToken\` object.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { sessionToken } = await authenticate.admin(
      request
    );
    return json(await getMyAppData({user: sessionToken.sub}));
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";

  const shopify = shopifyApp({
    // ...etc
    useOnlineTokens: true,
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Querying the GraphQL API

  ##### Description

  Use \`admin.graphql\` to make query / mutation requests.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { ActionFunctionArgs } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const action = async ({ request }: ActionFunctionArgs) => {
    const { admin } = await authenticate.admin(request);

    const response = await admin.graphql(
      `#graphql
      mutation populateProduct($input: ProductInput!) {
        productCreate(input: $input) {
          product {
            id
          }
        }
      }`,
      {
        variables: {
          input: { title: "Product Name" },
        },
      },
    );

    const productData = await response.json();
    return json({
      productId: productData.data?.productCreate?.product?.id,
    });
  }
  ```

  ##### /app/shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";

  const shopify = shopifyApp({
    // ...
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Handling GraphQL errors

  ##### Description

  Catch \`GraphqlQueryError\` errors to see error messages from the API.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { ActionFunctionArgs } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const action = async ({ request }: ActionFunctionArgs) => {
    const { admin } = await authenticate.admin(request);

    try {
      const response = await admin.graphql(
        `#graphql
        query incorrectQuery {
          products(first: 10) {
            nodes {
              not_a_field
            }
          }
        }`,
      );

      return json({ data: await response.json() });
    } catch (error) {
      if (error instanceof GraphqlQueryError) {
        // error.body.errors:
        // { graphQLErrors: [
        //   { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
        // ] }
        return json({ errors: error.body?.errors }, { status: 500 });
      }
      return json({ message: "An error occurred" }, { status: 500 });
    }
  }
  ```

  ##### /app/shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";

  const shopify = shopifyApp({
    // ...
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Using REST resources

  ##### Description

  Getting the number of orders in a store using REST resources. Visit the \[Admin REST API references]\(/docs/api/admin-rest) for examples on using each resource.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const {
      admin,
      session,
    } = await authenticate.admin(request);

    return json(
      admin.rest.resources.Order.count({ session }),
    );
  };
  ```

  ##### /app/shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";
  import { restResources } from "@shopify/shopify-api/rest/admin/2023-07";

  const shopify = shopifyApp({
    restResources,
    // ...etc
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Performing a GET request to the REST API

  ##### Description

  Use \`admin.rest.get\` to make custom requests to make a request to to the \`customer/count\` endpoint

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const {
      admin,
      session,
    } = await authenticate.admin(request);

    const response = await admin.rest.get({
      path: "/customers/count.json",
    });
    const customers = await response.json();

    return json({ customers });
  };
  ```

  ##### /app/shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";
  import { restResources } from "@shopify/shopify-api/rest/admin/2023-04";

  const shopify = shopifyApp({
    restResources,
    // ...etc
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Performing a POST request to the REST API

  ##### Description

  Use \`admin.rest.post\` to make custom requests to make a request to to the \`customers.json\` endpoint to send a welcome email

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, json } from "@remix-run/node";
  import { authenticate } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const {
      admin,
      session,
    } = await authenticate.admin(request);

    const response = admin.rest.post({
      path: "customers/7392136888625/send_invite.json",
      body: {
        customer_invite: {
          to: "new_test_email@shopify.com",
          from: "j.limited@example.com",
          bcc: ["j.limited@example.com"],
          subject: "Welcome to my new shop",
          custom_message: "My awesome new store",
        },
      },
    });

    const customerInvite = await response.json();
    return json({ customerInvite });
  };
  ```

  ##### /app/shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-remix/server";
  import { restResources } from "@shopify/shopify-api/rest/admin/2023-04";

  const shopify = shopifyApp({
    restResources,
    // ...etc
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Cancelling a subscription

  ##### Description

  Use the \`billing.cancel\` function to cancel an active subscription with the id returned from \`billing.require\`.

  ##### /app/routes/cancel-subscription.ts

  ```ts
  import { LoaderFunctionArgs } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    const billingCheck = await billing.require({
      plans: [MONTHLY_PLAN],
      onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
    });

    const subscription = billingCheck.appSubscriptions[0];
    const cancelledSubscription = await billing.cancel({
      subscriptionId: subscription.id,
      isTest: true,
      prorate: true,
     });

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        amount: 5,
        currencyCode: 'USD',
        interval: BillingInterval.Every30Days,
      },
      [ANNUAL_PLAN]: {
        amount: 50,
        currencyCode: 'USD',
        interval: BillingInterval.Annual,
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Check what billing plans a merchant is subscribed to

  ##### Description

  Use billing.check if you want to determine which plans are in use. Unlike \`require\`, \`check\` does notthrow an error if no active billing plans are present.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    const { hasActivePayment, appSubscriptions } = await billing.check({
      plans: [MONTHLY_PLAN],
      isTest: false,
    });
   console.log(hasActivePayment)
   console.log(appSubscriptions)
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        amount: 5,
        currencyCode: 'USD',
        interval: BillingInterval.Every30Days,
      },
      [ANNUAL_PLAN]: {
        amount: 50,
        currencyCode: 'USD',
        interval: BillingInterval.Annual,
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Using a custom return URL

  ##### Description

  Change where the merchant is returned to after approving the purchase using the \`returnUrl\` option.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    await billing.require({
      plans: [MONTHLY_PLAN],
      onFailure: async () => billing.request({
        plan: MONTHLY_PLAN,
        isTest: true,
        returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',
      }),
    });

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        amount: 5,
        currencyCode: 'USD',
        interval: BillingInterval.Every30Days,
      },
      [ANNUAL_PLAN]: {
        amount: 50,
        currencyCode: 'USD',
        interval: BillingInterval.Annual,
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Requesting billing right away

  ##### Description

  Call \`billing.request\` in the \`onFailure\` callback to immediately redirect to the Shopify page to request payment.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    await billing.require({
      plans: [MONTHLY_PLAN],
      isTest: true,
      onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
    });

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        amount: 5,
        currencyCode: 'USD',
        interval: BillingInterval.Every30Days,
      },
      [ANNUAL_PLAN]: {
        amount: 50,
        currencyCode: 'USD',
        interval: BillingInterval.Annual,
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Redirect to a plan selection page

  ##### Description

  When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs, redirect } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    const billingCheck = await billing.require({
      plans: [MONTHLY_PLAN, ANNUAL_PLAN],
      isTest: true,
      onFailure: () => redirect('/select-plan'),
    });

    const subscription = billingCheck.appSubscriptions[0];
    console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        amount: 5,
        currencyCode: 'USD',
        interval: BillingInterval.Every30Days,
      },
      [ANNUAL_PLAN]: {
        amount: 50,
        currencyCode: 'USD',
        interval: BillingInterval.Annual,
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Requesting billing with line items

  ##### Description

  Call \`billing.request\` with the \`v3\_lineItemBilling\` future flag enabled

  ##### /app/routes/\*\*\\/\*.ts

  ```ts
  import { LoaderFunctionArgs } from "@remix-run/node";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

  export const loader = async ({ request }: LoaderFunctionArgs) => {
    const { billing } = await authenticate.admin(request);
    await billing.require({
      plans: [MONTHLY_PLAN],
      isTest: true,
      onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
    });

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server";

  export const MONTHLY_PLAN = 'Monthly subscription';
  export const ANNUAL_PLAN = 'Annual subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        lineItems: [
         {
           amount: 5,
           currencyCode: 'USD',
           interval: BillingInterval.Every30Days,
          },
          {
           amount: 1,
           currencyCode: 'USD',
           interval: BillingInterval.Usage.
           terms: '1 dollar per 1000 emails',
          },
        ],
      },
    }
   future: {v3_lineItemBilling: true}
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

***

## Related

[Interact with the Admin API. - API context](https://shopify.dev/docs/api/shopify-app-remix/v2/apis/admin-api)

[Bill merchants for your app using the Admin API. - Billing context](https://shopify.dev/docs/api/shopify-app-remix/v2/apis/billing)

***
