# Webhook Contains functions for verifying Shopify webhooks. > Note: The format of the `admin` object returned by this function changes with the `v3_webhookAdminContext` future flag. Learn more about [gradual feature adoption](https://shopify.dev/docs/api/shopify-app-remix/guide-future-flags). ```typescript import {type ActionFunctionArgs} from '@remix-run/node'; import {authenticate} from '../shopify.server'; export const action = async ({request}: ActionFunctionArgs) => { const {topic, admin, payload} = await authenticate.webhook(request); switch (topic) { case 'PRODUCTS_UPDATE': await admin.graphql( `#graphql mutation setMetafield($productId: ID!, $time: String!) { metafieldsSet(metafields: { ownerId: $productId namespace: "my-app", key: "webhook_received_at", value: $time, type: "string", }) { metafields { key value } } } `, { variables: { productId: payload.admin_graphql_api_id, time: new Date().toISOString(), }, }, ); return new Response(); } throw new Response(); }; ``` ## authenticate.webhook Verifies requests coming from Shopify webhooks. ### AuthenticateWebhook #### Returns: Promise> #### Params: - request: Request export type AuthenticateWebhook< Future extends FutureFlagOptions, Resources extends ShopifyRestResources, Topics = string | number | symbol, > = (request: Request) => Promise>; ### WebhookContextWithoutSession ### session value: `undefined` ### admin value: `undefined` ### apiVersion value: `string` The API version used for the webhook. ### shop value: `string` The shop where the webhook was triggered. ### topic value: `Topics` The topic of the webhook. ### webhookId value: `string` A unique ID for the webhook. Useful to keep track of which events your app has already processed. ### payload value: `Record` The payload from the webhook request. ### subTopic value: `string` The sub-topic of the webhook. This is only available for certain webhooks. ### WebhookContextWithSession ### session value: `Session` A session with an offline token for the shop. Returned only if there is a session for the shop. ### admin value: `WebhookAdminContext` An admin context for the webhook. Returned only if there is a session for the shop. ### apiVersion value: `string` The API version used for the webhook. ### shop value: `string` The shop where the webhook was triggered. ### topic value: `Topics` The topic of the webhook. ### webhookId value: `string` A unique ID for the webhook. Useful to keep track of which events your app has already processed. ### payload value: `Record` The payload from the webhook request. ### subTopic value: `string` The sub-topic of the webhook. This is only available for certain webhooks. ### Session Stores App information from logged in merchants so they can make authenticated requests to the Admin API. ### id value: `string` The unique identifier for the session. ### shop value: `string` The Shopify shop domain, such as `example.myshopify.com`. ### state value: `string` The state of the session. Used for the OAuth authentication code flow. ### isOnline value: `boolean` Whether the access token in the session is online or offline. ### scope value: `string` The desired scopes for the access token, at the time the session was created. ### expires value: `Date` The date the access token expires. ### accessToken value: `string` The access token for the session. ### onlineAccessInfo value: `OnlineAccessInfo` Information on the user for the session. Only present for online sessions. ### isActive value: `(scopes: string | string[] | AuthScopes) => boolean` Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes. ### isScopeChanged value: `(scopes: string | string[] | AuthScopes) => boolean` Whether the access token has the given scopes. ### isExpired value: `(withinMillisecondsOfExpiry?: number) => boolean` Whether the access token is expired. ### toObject value: `() => SessionParams` Converts an object with data into a Session. ### equals value: `(other: Session) => boolean` Checks whether the given session is equal to this session. ### toPropertyArray value: `(returnUserData?: boolean) => [string, string | number | boolean][]` Converts the session into an array of key-value pairs. ### OnlineAccessInfo ### expires_in value: `number` How long the access token is valid for, in seconds. ### associated_user_scope value: `string` The effective set of scopes for the session. ### associated_user value: `OnlineAccessUser` The user associated with the access token. ### OnlineAccessUser ### id value: `number` The user's ID. ### first_name value: `string` The user's first name. ### last_name value: `string` The user's last name. ### email value: `string` The user's email address. ### email_verified value: `boolean` Whether the user has verified their email address. ### account_owner value: `boolean` Whether the user is the account owner. ### locale value: `string` The user's locale. ### collaborator value: `boolean` Whether the user is a collaborator. ### AuthScopes A class that represents a set of access token scopes. ### has value: `(scope: string | string[] | AuthScopes) => boolean` Checks whether the current set of scopes includes the given one. ### equals value: `(otherScopes: string | string[] | AuthScopes) => boolean` Checks whether the current set of scopes equals the given one. ### toString value: `() => string` Returns a comma-separated string with the current set of scopes. ### toArray value: `() => any[]` Returns an array with the current set of scopes. ### SessionParams ### [key: string] value: `any` ### id value: `string` The unique identifier for the session. ### shop value: `string` The Shopify shop domain. ### state value: `string` The state of the session. Used for the OAuth authentication code flow. ### isOnline value: `boolean` Whether the access token in the session is online or offline. ### scope value: `string` The scopes for the access token. ### expires value: `Date` The date the access token expires. ### accessToken value: `string` The access token for the session. ### onlineAccessInfo value: `OnlineAccessInfo | StoredOnlineAccessInfo` Information on the user for the session. Only present for online sessions. ### NonEmbeddedAdminContext ### session value: `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. ### admin value: `AdminApiContext` Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request. ### billing value: `BillingContext` Billing methods for this store, based on the plans defined in the `billing` config option. ### cors value: `EnsureCORSFunction` A function that ensures the CORS headers are set correctly for the response. ### AdminApiContext ### rest value: `RestClientWithResources` 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. ### graphql value: `GraphQLClient` Methods for interacting with the Shopify Admin GraphQL API ### RemixRestClient ### session value: `Session` ### get value: `(params: GetRequestParams) => Promise` Performs a GET request on the given path. ### post value: `(params: PostRequestParams) => Promise` Performs a POST request on the given path. ### put value: `(params: PostRequestParams) => Promise` Performs a PUT request on the given path. ### delete value: `(params: GetRequestParams) => Promise` Performs a DELETE request on the given path. ### GetRequestParams ### path value: `string` The path to the resource, relative to the API version root. ### type value: `DataType` The type of data expected in the response. ### data value: `string | Record` The request body. ### query value: `SearchParams` Query parameters to be sent with the request. ### extraHeaders value: `HeaderParams` Additional headers to be sent with the request. ### tries value: `number` The maximum number of times the request can be made if it fails with a throttling or server error. ### DataType ### JSON value: `application/json` ### GraphQL value: `application/graphql` ### URLEncoded value: `application/x-www-form-urlencoded` ### GraphQLClient #### Returns: interface Promise { /** * 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(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; /** * 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(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; }, interface Promise {}, Promise: PromiseConstructor, interface Promise { readonly [Symbol.toStringTag]: string; }, interface Promise { /** * 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; } #### Params: - query: Operation extends keyof Operations - options: GraphQLQueryOptions export type GraphQLClient = < Operation extends keyof Operations, >( query: Operation, options?: GraphQLQueryOptions, ) => Promise>; ### GraphQLQueryOptions ### variables value: `ApiClientRequestOptions["variables"]` The variables to pass to the operation. ### apiVersion value: `ApiVersion` The version of the API to use for the request. ### headers value: `Record` Additional headers to include in the request. ### tries value: `number` The total number of times to try the request if it fails. ### ApiVersion ### October22 value: `2022-10` ### January23 value: `2023-01` ### April23 value: `2023-04` ### July23 value: `2023-07` ### October23 value: `2023-10` ### January24 value: `2024-01` ### April24 value: `2024-04` ### Unstable value: `unstable` ### BillingContext ### require value: `(options: RequireBillingOptions) => Promise` Checks if the shop has an active payment for any plan defined in the `billing` config option. ### check value: `(options: CheckBillingOptions) => Promise` Checks if the shop has an active payment for any plan defined in the `billing` config option. ### request value: `(options: RequestBillingOptions) => Promise` Requests payment for the plan. ### cancel value: `(options: CancelBillingOptions) => Promise` Cancels an ongoing subscription, given its ID. ### RequireBillingOptions ### plans value: `(keyof Config["billing"])[]` The plans to check for. Must be one of the values defined in the `billing` config option. ### onFailure value: `(error: any) => Promise` How to handle the request if the shop doesn't have an active payment for any plan. ### isTest value: `boolean` Whether to consider test purchases. ### BillingCheckResponseObject ### hasActivePayment value: `boolean` Whether the user has an active payment method. ### oneTimePurchases value: `OneTimePurchase[]` The one-time purchases the shop has. ### appSubscriptions value: `AppSubscription[]` The active subscriptions the shop has. ### OneTimePurchase ### id value: `string` The ID of the one-time purchase. ### name value: `string` The name of the purchased plan. ### test value: `boolean` Whether this is a test purchase. ### status value: `string` The status of the one-time purchase. ### AppSubscription ### id value: `string` The ID of the app subscription. ### name value: `string` The name of the purchased plan. ### test value: `boolean` Whether this is a test subscription. ### lineItems value: `ActiveSubscriptionLineItem[]` ### ActiveSubscriptionLineItem ### id value: `string` ### plan value: `AppPlan` ### AppPlan ### pricingDetails value: `RecurringAppPlan | UsageAppPlan` ### RecurringAppPlan ### interval value: `BillingInterval.Every30Days | BillingInterval.Annual` ### price value: `Money` ### discount value: `AppPlanDiscount` ### BillingInterval ### OneTime value: `ONE_TIME` ### Every30Days value: `EVERY_30_DAYS` ### Annual value: `ANNUAL` ### Usage value: `USAGE` ### Money ### amount value: `number` ### currencyCode value: `string` ### AppPlanDiscount ### durationLimitInIntervals value: `number` ### remainingDurationInIntervals value: `number` ### priceAfterDiscount value: `Money` ### value value: `AppPlanDiscountAmount` ### BillingConfigSubscriptionPlanDiscountAmount ### amount value: `number` The amount to discount. Cannot be set if `percentage` is set. ### percentage value: `never` The percentage to discount. Cannot be set if `amount` is set. ### BillingConfigSubscriptionPlanDiscountPercentage ### amount value: `never` The amount to discount. Cannot be set if `percentage` is set. ### percentage value: `number` The percentage to discount. Cannot be set if `amount` is set. ### UsageAppPlan ### balanceUsed value: `Money` ### cappedAmount value: `Money` ### terms value: `string` ### CheckBillingOptions ### plans value: `(keyof Config["billing"])[]` The plans to check for. Must be one of the values defined in the `billing` config option. ### isTest value: `boolean` Whether to consider test purchases. ### RequestBillingOptions ### plan value: `keyof Config["billing"]` The plan to request. Must be one of the values defined in the `billing` config option. ### isTest value: `boolean` Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged. ### returnUrl value: `string` The URL to return to after the merchant approves the payment. ### CancelBillingOptions ### subscriptionId value: `string` The ID of the subscription to cancel. ### prorate value: `boolean` Whether to prorate the cancellation. ### isTest value: `boolean` ### EmbeddedAdminContext ### sessionToken value: `JwtPayload` The decoded and validated session token for the request. Returned only if `isEmbeddedApp` is `true`. ### redirect value: `RedirectFunction` 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`. ### session value: `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. ### admin value: `AdminApiContext` Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request. ### billing value: `BillingContext` Billing methods for this store, based on the plans defined in the `billing` config option. ### cors value: `EnsureCORSFunction` A function that ensures the CORS headers are set correctly for the response. ### JwtPayload ### iss value: `string` The shop's admin domain. ### dest value: `string` The shop's domain. ### aud value: `string` The client ID of the receiving app. ### sub value: `string` The User that the session token is intended for. ### exp value: `number` When the session token expires. ### nbf value: `number` When the session token activates. ### iat value: `number` When the session token was issued. ### jti value: `string` A secure random UUID. ### sid value: `string` A unique session ID per user and app. ### RedirectFunction #### Returns: TypedResponse #### Params: - url: string - init: RedirectInit export type RedirectFunction = ( url: string, init?: RedirectInit, ) => TypedResponse; ### LegacyWebhookAdminApiContext ### rest value: `RestClient & Resources` A REST client. ### graphql value: `InstanceType` A GraphQL client. ### RestClient ### loggedDeprecations value: `Record` ### client value: `AdminRestApiClient` ### session value: `Session` ### apiVersion value: `ApiVersion` ### get value: `(params: GetRequestParams) => Promise>` Performs a GET request on the given path. ### post value: `(params: PostRequestParams) => Promise>` Performs a POST request on the given path. ### put value: `(params: PostRequestParams) => Promise>` Performs a PUT request on the given path. ### delete value: `(params: GetRequestParams) => Promise>` Performs a DELETE request on the given path. ### RestRequestReturn ### body value: `T` ### headers value: `Headers` ### pageInfo value: `PageInfo` ### PageInfo ### limit value: `string` ### fields value: `string[]` ### previousPageUrl value: `string` ### nextPageUrl value: `string` ### prevPage value: `PageInfoParams` ### nextPage value: `PageInfoParams` ### PageInfoParams ### path value: `string` ### query value: `SearchParams` ## Related - [Admin API context](https://shopify.dev/docs/api/shopify-app-remix/apis/admin-api) ## Examples Contains functions for verifying Shopify webhooks. > Note: The format of the `admin` object returned by this function changes with the `v3_webhookAdminContext` future flag. Learn more about [gradual feature adoption](https://shopify.dev/docs/api/shopify-app-remix/guide-future-flags). ### admin ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionFunctionArgs) { const { admin } = await authenticate.webhook(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({ data: productData.data }); } ``` ```typescript import { json, ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionFunctionArgs) { const { admin } = await authenticate.webhook(request); const response = await admin?.graphql.query({ data: { query: `#graphql mutation populateProduct($input: ProductInput!) { productCreate(input: $input) { product { id } } }`, variables: { input: { title: "Product Name" } }, }, }); const productData = response?.body.data; return json({ data: productData.data }); } ``` ### apiVersion ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { apiVersion } = await authenticate.webhook(request); return new Response(); }; ``` ### shop ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { shop } = await authenticate.webhook(request); return new Response(); }; ``` ### topic ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { topic } = await authenticate.webhook(request); switch (topic) { case "APP_UNINSTALLED": // Do something when the app is uninstalled. break; } return new Response(); }; ``` ### webhookId ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { webhookId } = await authenticate.webhook(request); return new Response(); }; ``` ### payload ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { payload } = await authenticate.webhook(request); return new Response(); }; ``` ### subTopic ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const { subTopic } = await authenticate.webhook(request); return new Response(); }; ```