From 61f1b65cbcd9d5d987ab309cd35a9eba758f695c Mon Sep 17 00:00:00 2001 From: Haozhi Liao Date: Thu, 22 Oct 2026 10:01:49 -0400 Subject: [PATCH] Add appEvents.emit to the Checkout and Customer Account APIs Adds shopify.appEvents.emit(eventHandle, attributes?) to the Checkout standard and address-autocomplete APIs and the Customer Account standard API. emit resolves with {success: true} once the host accepts the event, or {success: false, error} when it was not sent. Also: - Simplify appEvents API doc wording - Make appEvents required on the Checkout and Customer Account APIs - Rename AppEventsApi to AppEvents - Remove the unused AppEvent type --- .../app-events-checkout-customer-account.md | 5 ++ .../src/checkout/factories.ts | 1 + .../src/customer-account/factories.ts | 1 + .../ui-extensions/src/surfaces/checkout.ts | 4 ++ .../api/address-autocomplete/standard.ts | 6 +++ .../checkout/api/standard/standard.ts | 50 +++++++++++++++++++ .../src/surfaces/customer-account/api.ts | 4 ++ .../surfaces/customer-account/api/shared.ts | 10 ++++ .../api/standard-api/standard-api.ts | 6 +++ 9 files changed, 87 insertions(+) create mode 100644 .changeset/app-events-checkout-customer-account.md diff --git a/.changeset/app-events-checkout-customer-account.md b/.changeset/app-events-checkout-customer-account.md new file mode 100644 index 0000000000..7bcaa31c40 --- /dev/null +++ b/.changeset/app-events-checkout-customer-account.md @@ -0,0 +1,5 @@ +--- +'@shopify/ui-extensions': minor +--- + +`appEvents`: add `shopify.appEvents.emit(eventHandle, attributes?)` to the Checkout standard and address-autocomplete APIs and the Customer Account standard API, for reporting app events to Shopify diff --git a/packages/ui-extensions-tester/src/checkout/factories.ts b/packages/ui-extensions-tester/src/checkout/factories.ts index b7dc095374..9b30cfc2ed 100644 --- a/packages/ui-extensions-tester/src/checkout/factories.ts +++ b/packages/ui-extensions-tester/src/checkout/factories.ts @@ -39,6 +39,7 @@ function createSharedApiMocks() { visitor: async () => ({type: 'success' as const}), }, i18n: createMockI18n(), + appEvents: {emit: async () => ({success: true as const})}, sessionToken: {get: async () => 'mock-session-token'}, shop: { id: 'gid://shopify/Shop/1', diff --git a/packages/ui-extensions-tester/src/customer-account/factories.ts b/packages/ui-extensions-tester/src/customer-account/factories.ts index 4e9050d66d..e5726883bd 100644 --- a/packages/ui-extensions-tester/src/customer-account/factories.ts +++ b/packages/ui-extensions-tester/src/customer-account/factories.ts @@ -51,6 +51,7 @@ function createStandardApiProperties(target: T) { }, i18n: createMockI18n(), storage: createStorage(), + appEvents: {emit: async () => ({success: true as const})}, sessionToken: {get: async () => 'mock-session-token'}, analytics: { publish: async () => true, diff --git a/packages/ui-extensions/src/surfaces/checkout.ts b/packages/ui-extensions/src/surfaces/checkout.ts index 1519936c3f..a343001865 100644 --- a/packages/ui-extensions/src/surfaces/checkout.ts +++ b/packages/ui-extensions/src/surfaces/checkout.ts @@ -27,6 +27,10 @@ export type { Shop, Version, Metafield, + AppEventAttributeValue, + AppEventAttributes, + AppEventEmitResult, + AppEvents, AppMetafield, AppMetafieldEntryTarget, AppMetafieldEntry, diff --git a/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts b/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts index b389d86165..7dc7af3e1e 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts @@ -1,5 +1,6 @@ import type { Analytics, + AppEvents, AppMetafieldEntry, Capability, CheckoutToken, @@ -48,6 +49,11 @@ export interface AddressAutocompleteStandardApi< */ analytics: Analytics; + /** + * Reports events to Shopify for this app. + */ + appEvents: AppEvents; + /** * The custom attributes left by the customer to the merchant, either in their cart or during checkout. */ diff --git a/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts b/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts index 3e1d2f3eaa..1103d6bc54 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts @@ -584,6 +584,11 @@ export interface StandardApi { */ analytics: Analytics; + /** + * Reports events to Shopify for this app. + */ + appEvents: AppEvents; + /** * The gift cards that have been applied to the checkout. Each entry includes * the last four characters of the gift card code, the amount used at @@ -1690,6 +1695,51 @@ export interface Analytics { */ visitor(data: {email?: string; phone?: string}): Promise; } + +/** + * A value attached to an app event. + * @publicDocs + */ +export type AppEventAttributeValue = string | number | boolean; + +/** + * Attributes related to an app event. + * @publicDocs + */ +export type AppEventAttributes = Record; + +/** + * The outcome of an `emit` call. + * + * `success: true` means the host accepted the event. It does not confirm delivery. + * + * `success: false` means the event was not sent. `error` describes why. + * @publicDocs + */ +export type AppEventEmitResult = + | {readonly success: true} + | {readonly success: false; readonly error: string}; + +/** + * Reports events to Shopify for this app. + * @publicDocs + */ +export interface AppEvents { + /** + * Reports one occurrence of an app-defined event. + * + * @param eventHandle The app-defined event name. + * @param attributes Attributes related to this event. + * @default {} + * @returns A promise that resolves with the outcome once the host has + * handled the call. + */ + emit( + eventHandle: string, + attributes?: AppEventAttributes, + ): Promise; +} + /** * Represents a visitor result. * @publicDocs diff --git a/packages/ui-extensions/src/surfaces/customer-account/api.ts b/packages/ui-extensions/src/surfaces/customer-account/api.ts index 17b1521a7b..a47d253fee 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api.ts @@ -79,6 +79,10 @@ export type { VisitorSuccess, VisitorError, SubscribableSignalLike, + AppEventAttributeValue, + AppEventAttributes, + AppEventEmitResult, + AppEvents, } from './api/shared'; export type {CartLineItemApi} from './api/cart-line/cart-line-item'; diff --git a/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts b/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts index 86b2d7b137..d5d856934f 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts @@ -11,6 +11,12 @@ import type { } from '../../../shared'; import type {SubscribableSignalLike} from '../../checkout/shared'; +import type { + AppEventAttributeValue, + AppEventAttributes, + AppEventEmitResult, + AppEvents, +} from '../../checkout/api/standard/standard'; export { ApiVersion, @@ -21,6 +27,10 @@ export { GraphQLError, StorefrontApiVersion, SubscribableSignalLike, + AppEventAttributeValue, + AppEventAttributes, + AppEventEmitResult, + AppEvents, }; /** diff --git a/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts b/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts index 7c94e59d45..de25266280 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts @@ -14,6 +14,7 @@ import { ToastApi, Intents, SubscribableSignalLike, + AppEvents, } from '../shared'; import type {ExtensionTarget} from '../../extension-targets'; @@ -78,6 +79,11 @@ export interface StandardApi { */ sessionToken: SessionToken; + /** + * Reports events to Shopify for this app. + */ + appEvents: AppEvents; + /** * Tracks custom events and sends visitor information to [web pixels](/docs/apps/build/marketing-analytics/pixels). Use `publish()` to emit events and `visitor()` to submit visitor data. *