---
title: Admin
description: Contains methods for authenticating and interacting with the Admin API.
api_version: v0
source_url:
  html: 'https://shopify.dev/docs/api/shopify-app-react-router/v0/authenticate/admin'
  md: >-
    https://shopify.dev/docs/api/shopify-app-react-router/v0/authenticate/admin.md
---

# Admin

Contains methods 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.

**Note:** The shape of the returned object changes depending on the \<code>distribution\</code> config. Merchant custom apps (\<code>\<span class="PreventFireFoxApplyingGapToWBR">App\<wbr/>Distribution.Shopify\<wbr/>Admin\</span>\</code>) are not embedded so do not return session tokens or redirect functionality. All other distributions are embedded and so they return a context with session tokens and redirect functionality.

### Parameters

* **request**

  **Request**

  **required**

### Returns

* **Promise\<AdminContext\<Config>>**

### AdminContext

```ts
EmbeddedTypedAdminContext<Config> & ScopesContext
```

### EmbeddedTypedAdminContext

```ts
Config['distribution'] extends AppDistribution.ShopifyAdmin
    ? NonEmbeddedAdminContext<Config>
    : EmbeddedAdminContext<Config>
```

### AppDistribution

* AppStore

  ```ts
  app_store
  ```

* SingleMerchant

  ```ts
  single_merchant
  ```

* ShopifyAdmin

  ```ts
  shopify_admin
  ```

### NonEmbeddedAdminContext

* admin

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

  ```ts
  AdminApiContext
  ```

* 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>
  ```

### 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>
  ```

* signal

  An optional AbortSignal to cancel the request.

  ```ts
  AbortSignal
  ```

* 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>["variables"]
  ```

### 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 extends CheckBillingOptions<Config>>(options?: Options) => Promise<BillingCheckResponseObject>
  ```

* createUsageRecord

  Creates a usage record for an app subscription.

  ```ts
  (options: CreateUsageRecordOptions) => Promise<UsageRecord>
  ```

* 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>
  ```

* updateUsageCappedAmount

  Updates the capped amount for a usage billing plan.

  ```ts
  (options: UpdateUsageCappedAmountOptions) => Promise<never>
  ```

### CancelBillingOptions

* isTest

  Whether to use the test mode. This prevents the credit card from being charged.

  ```ts
  boolean
  ```

* prorate

  Whether to issue prorated credits for the unused portion of the app subscription. There will be a corresponding deduction (based on revenue share) to your Partner account. For example, if a $10.00 app subscription (with 0% revenue share) is cancelled and prorated half way through the billing cycle, then the merchant will be credited $5.00 and that amount will be deducted from your Partner account.

  ```ts
  boolean
  ```

* subscriptionId

  The ID of the subscription to cancel.

  ```ts
  string
  ```

### Options

* layout

  Whether to use the shop's theme layout around the Liquid content.

  ```ts
  boolean
  ```

### 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"])[]
  ```

### CreateUsageRecordOptions

* description

  The description of the app usage record.

  ```ts
  string
  ```

* idempotencyKey

  ```ts
  string
  ```

* isTest

  Whether to use the test mode. This prevents the credit card from being charged.

  ```ts
  boolean
  ```

* price

  The price of the app usage record.

  ```ts
  { amount: number; currencyCode: string; }
  ```

* subscriptionLineItemId

  ```ts
  string
  ```

### 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"])[]
  ```

### UpdateUsageCappedAmountOptions

* cappedAmount

  The maximum charge for the usage billing plan.

  ```ts
  { amount: number; currencyCode: string; }
  ```

* subscriptionLineItemId

  The subscription line item ID to update.

  ```ts
  string
  ```

### EnsureCORSFunction



### EmbeddedAdminContext

* admin

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

  ```ts
  AdminApiContext
  ```

* 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 for embedded apps (all apps except merchant custom apps).

  ```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 for embedded apps (all distribution types except merchant custom apps)

  ```ts
  JwtPayload
  ```

### RedirectFunction

* url

  ```ts
  string
  ```

* init

  ```ts
  RedirectInit
  ```

returns

```ts
Response
```

### RedirectInit

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

### RedirectTarget

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

### ScopesContext

* scopes

  Methods to manage scopes for the store that made the request.

  ```ts
  ScopesApiContext
  ```

### ScopesApiContext

Provides utilities that apps can use to \[manage scopes]\(https://shopify.dev/docs/apps/build/authentication-authorization/app-installation/manage-access-scopes) for the app using the Admin API.

* query

  Queries Shopify for the scopes for this app on this shop

  ```ts
  () => Promise<ScopesDetail>
  ```

* request

  Requests the merchant to grant the provided scopes for this app on this shop Warning: This method performs a server-side redirect.

  ```ts
  (scopes: string[]) => Promise<void>
  ```

* revoke

  Revokes the provided scopes from this app on this shop Warning: This method throws an \[error]\(https://shopify.dev/docs/api/admin-graphql/unstable/objects/AppRevokeAccessScopesAppRevokeScopeError) if the provided optional scopes contains a required scope.

  ```ts
  (scopes: string[]) => Promise<ScopesRevokeResponse>
  ```

### ScopesDetail

* granted

  The scopes that have been granted on the shop for this app

  ```ts
  string[]
  ```

* optional

  The optional scopes that the app has declared in its configuration

  ```ts
  string[]
  ```

* required

  The required scopes that the app has declared in its configuration

  ```ts
  string[]
  ```

### ScopesRevokeResponse

* revoked

  The scopes that have been revoked on the shop for this app

  ```ts
  string[]
  ```

Examples

### Examples

* ####

  ##### Description

  Authenticate, run API mutation, and redirect

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

  ```ts
  import {type ActionFunctionArgs, data} from 'react-router';
  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 data({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 "react-router";
  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(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 "react-router";
  import { authenticate } from "../shopify.server";

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

* ####

  ##### Description

  Redirects to a product page in the Shopify admin. Pass in a \`target\` option of \`\_top\` or \`\_parent\` to navigate in the current window, or \`\_blank\` to open a new tab.

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

  ```ts
  import { LoaderFunctionArgs, json } from "react-router";
  import { authenticate } from "../shopify.server";

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

* ####

  ##### Description

  Pass in a \`target\` option of \`\_top\` or \`\_parent\` to navigate in the current window, or \`\_blank\` to open a new tab.

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

  ```ts
  import { LoaderFunctionArgs, json } from "react-router";
  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 "react-router";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

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

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-react-router/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 "react-router";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

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

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-react-router/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 "react-router";
  import { authenticate } from "../shopify.server";
  import { getMyAppData } from "~/db/model.server";

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

  ##### shopify.server.ts

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-react-router/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 "react-router";
  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 ({
      productId: productData.data?.productCreate?.product?.id,
    });
  }
  ```

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

  ```ts
  import { shopifyApp } from "@shopify/shopify-app-react-router/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 "react-router";
  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 ({ 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 ({ errors: error.body?.errors }, { status: 500 });
      }
      return ({ message: "An error occurred" }, { status: 500 });
    }
  }
  ```

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

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

  const shopify = shopifyApp({
    // ...
  });
  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 "react-router";
  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-react-router/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,
          }
        ],
      },
      [ANNUAL_PLAN]: {
        lineItems: [
          {
            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 "react-router";
  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-react-router/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,
          }
        ],
      },
      [ANNUAL_PLAN]: {
        lineItems: [
          {
            amount: 50,
            currencyCode: 'USD',
            interval: BillingInterval.Annual,
          }
        ],
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* ####

  ##### Description

  Use billing.check to see if any payments exist for the store, regardless of whether it's a test ormatches one or more plans.

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

  ```ts
  import { LoaderFunctionArgs } from "react-router";
  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();
    // This will be true if any payment is found
    console.log(hasActivePayment);
    console.log(appSubscriptions);
  };
  ```

* #### Creating a usage record

  ##### Description

  Create a usage record for the active usage billing plan

  ##### /app/routes/create-usage-record.ts

  ```ts
  import { ActionFunctionArgs } from "react-router";
  import { authenticate, MONTHLY_PLAN } from "../shopify.server";

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

    const chargeBilling = await billing.createUsageRecord({
       description: "Usage record for product creation",
       price: {
         amount: 1,
         currencyCode: "USD",
        },
       isTest: true,
     });
   console.log(chargeBilling);

    // App logic
  };
  ```

  ##### shopify.server.ts

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

  export const USAGE_PLAN = 'Usage subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [USAGE_PLAN]: {
        lineItems: [
          {
            amount: 5,
            currencyCode: 'USD',
            interval: BillingInterval.Usage,
          }
        ],
      },
    }
  });
  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 "react-router";
  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-react-router/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,
          }
        ],
      },
      [ANNUAL_PLAN]: {
        lineItems: [
          {
            amount: 50,
            currencyCode: 'USD',
            interval: BillingInterval.Annual,
          }
        ],
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Overriding plan settings

  ##### Description

  Customize the plan for a merchant when requesting billing. Any fields from the plan can be overridden, as long as the billing interval for line items matches the config.

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

  ```ts
  import { LoaderFunctionArgs } from "react-router";
  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,
        trialDays: 14,
        lineItems: [
          {
            interval: BillingInterval.Every30Days,
            discount: { value: { percentage: 0.1 } },
          },
        ],
      }),
    });

    // App logic
  };
  ```

  ##### shopify.server.ts

  ```ts
  import { shopifyApp, BillingInterval } from "@shopify/shopify-app-react-router/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,
          }
        ],
      },
      [ANNUAL_PLAN]: {
        lineItems: [
          {
            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 "react-router";
  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-react-router/server";

  export const MONTHLY_PLAN = 'Monthly subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [MONTHLY_PLAN]: {
        lineItems: [
          {
            amount: 5,
            currencyCode: 'USD',
            interval: BillingInterval.Every30Days,
          }
        ],
      },
    }
  });
  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 "react-router";
  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-react-router/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,
          }
        ],
      },
      [ANNUAL_PLAN]: {
        lineItems: [
          {
            amount: 50,
            currencyCode: 'USD',
            interval: BillingInterval.Annual,
          }
        ],
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* #### Updating the capped amount for a usage billing plan

  ##### Description

  Update the capped amount for the usage billing plan specified by \`subscriptionLineItemId\`.

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

  ```ts
  import { ActionFunctionArgs } from "react-router";
  import { authenticate } from "../shopify.server";

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

    await billing.updateUsageCappedAmount({
      subscriptionLineItemId: "gid://shopify/AppSubscriptionLineItem/12345?v=1&index=1",
      cappedAmount: {
        amount: 10,
        currencyCode: "USD"
      },
    });

    // App logic
  };
  ```

  ##### shopify.server.ts

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

  export const USAGE_PLAN = 'Usage subscription';

  const shopify = shopifyApp({
    // ...etc
    billing: {
      [USAGE_PLAN]: {
        lineItems: [
          {
            amount: 5,
            currencyCode: 'USD',
            interval: BillingInterval.Usage,
            terms: "Usage based"
          }
        ],
      },
    }
  });
  export default shopify;
  export const authenticate = shopify.authenticate;
  ```

* ####

  ##### Description

  Call \`scopes.query\` to get scope details.

  ##### /app.\_index.tsx

  ```ts
  import type { LoaderFunctionArgs } from "react-router";
  import { useLoaderData } from "react-router";
  import { authenticate } from "../shopify.server";
  import { json } from "react-router";

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

    const scopesDetail =  await scopes.query();

    return ({
      hasWriteProducts: scopesDetail.granted.includes('write_products'),
    });
  };

  export default function Index() {
    const {hasWriteProducts} = useLoaderData<typeof loader>();

    ...
  }
  ```

* ####

  ##### Description

  Call \`scopes.request\` to request optional scopes.

  ##### /app/routes/app.request.tsx

  ```ts
  import type { ActionFunctionArgs } from "react-router";
  import { useFetcher } from "react-router";
  import { authenticate } from "../shopify.server";
  import { json } from "react-router";

  // Example of an action to POST a request to for optional scopes
  export const action = async ({ request }: ActionFunctionArgs) => {
    const { scopes } = await authenticate.admin(request);

    const body = await request.formData();
    const scopesToRequest = body.getAll("scopes") as string[];

    // If the scopes are not already granted, a full page redirect to the request URL occurs
    await scopes.request(scopesToRequest);
    // otherwise return an empty response
    return ({});
  };

  export default function Index() {
    const fetcher = useFetcher<typeof action>();

    const handleRequest = () => {
      fetcher.submit({scopes: ["write_products"]}, {
        method: "POST",
      });
    };

    ...
  }
  ```

* ####

  ##### Description

  Call \`scopes.revoke\` to revoke optional scopes.

  ##### /app.\_index.tsx

  ```ts
  import type { ActionFunctionArgs } from "react-router";
  import { useFetcher } from "react-router";
  import { authenticate } from "../shopify.server";
  import { json } from "react-router";

  // Example of an action to POST optional scopes to revoke
  export const action = async ({ request }: ActionFunctionArgs) => {
    const { scopes } = await authenticate.admin(request);

    const body = await request.formData();
    const scopesToRevoke = body.getAll("scopes") as string[];

    const revokedResponse = await scopes.revoke(scopesToRevoke);

    return (revokedResponse);
  };

  export default function Index() {
    const fetcher = useFetcher<typeof action>();

    const handleRevoke = () => {
      fetcher.submit({scopes: ["write_products"]}, {
        method: "POST",
      });
    };

    ...
  }
  ```

***

## Related

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

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

***
