ExShopifyApp.AppEvents (ex_shopify_app v1.2.0)

Client for the Shopify App Events API, used to report usage events.

Shopify maps each reported event to the meter configured on the app's pricing plans, keyed by event_handle (which must match a meter handle exactly). Some meters drive billing and others are tracking-only; this client is agnostic to that distinction — the host app decides which meter does what, and owns the idempotency-key strategy.

Authentication uses the app's Dev Dashboard credentials (ExShopifyApp.api_key/0 / ExShopifyApp.api_secret/0) via the client_credentials grant — see fetch_token/0 for the raw request. The resulting JWT is valid ~60 min and cached by the configured ExShopifyApp.AppEvents.TokenCache implementation (default ExShopifyApp.AppEvents.TokenServer) until shortly before it expires.

Docs: https://shopify.dev/docs/apps/build/app-events

Summary

Functions

Fetches a fresh App Events access token via the client_credentials grant.

Reports a usage event to Shopify.

Functions

fetch_token()

@spec fetch_token() :: {:ok, String.t(), non_neg_integer()} | {:error, any()}

Fetches a fresh App Events access token via the client_credentials grant.

This is the raw primitive: it performs the token request and returns {:ok, token, expires_in_seconds}, where expires_in_seconds is derived from the JWT's exp claim (the endpoint returns only the token, not an expires_in field). Returns {:error, %Tesla.Env{}} on a non-200 (or a 200 without a decodable token), or {:error, reason} on a transport error. It does no caching — for cached access use the configured ExShopifyApp.AppEvents.TokenCache (default ExShopifyApp.AppEvents.TokenServer), which wraps this.

report(event, map)

@spec report(ExShopifyApp.AppEvents.Event.t(), ExShopifyApp.Shop.identified()) ::
  {:ok, :accepted} | {:error, any()}

Reports a usage event to Shopify.

event is an ExShopifyApp.AppEvents.Event — see that struct for field semantics, in particular the idempotency-key contract. shop is any map carrying the shop's :shop_gid (see ExShopifyApp.Shop.identified/0 and ExShopifyApp.Graphql.ensure_gid/2). The event comes first so builders pipe in:

Event.new(%{event_handle: "orders_processed", value: 1, idempotency_key: order_gid})
|> AppEvents.report(shop)

The API acknowledges receipt with 202 regardless of billing validation. Returns {:ok, :accepted} on 202, {:error, %Tesla.Env{}} on any other response, or {:error, reason} on a transport error.