Skip to main content

App Events API reference

The App Events API lets developers send events that happen in their app to Shopify. All events are shown in the Dev Dashboard logs section. Events configured as part of Shopify App Pricing are also processed for billing. Declare standard or custom events to make app activity available in Shopify Analytics.

All App Events API requests require a valid bearer token.

API keys created in the Dev Dashboard provide a client ID and secret that can be used to generate a JWT token. Permissions to write to App Events are automatically included — no additional scope configuration is required.

POST

https://api.shopify.com/auth/access_token

const response = await fetch("https://api.shopify.com/auth/access_token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: "{your_client_id}",
client_secret: "{your_client_secret}",
grant_type: "client_credentials",
}),
});

const data = await response.json();

The response will contain:

  • access_token: A JWT access token that can be used to interact with the App Events API.
  • token_type: The type of the access token. This is always Bearer.

If a request returns a 403 response with the message You do not have permission to access this website, then make sure the request includes a User-Agent header.

{} Response

{
"access_token": "f8563253df0bf277ec9ac6f649fc3f17",
"token_type": "Bearer"
}

Include your token as a Authorization: Bearer {token} header on all API requests. Access tokens expire after 60 minutes. You can use a JWT decoder tool like jwt.io to investigate more details related to how Shopify issues this token. Request a new token before the current one expires.

POST

https://api.shopify.com/app/2027-01/events

const response = await fetch("https://api.shopify.com/app/2027-01/events", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer {BEARER_TOKEN}",
},
body: JSON.stringify({
shop_id: "gid://shopify/Shop/23423423",
event_handle: "sms_sent",
timestamp: "2026-01-27T14:30:00Z",
idempotency_key: "evt_55667788",
attributes: {
value: 1,
},
}),
});

const data = await response.json();

Anchor to Endpoints and requestsEndpoints and requests

The App Events API lets you send events happening in your app to Shopify. All events are visible in the Dev Dashboard. Events sent to this API as part of Shopify App Pricing are processed for billing to merchants. Each request sends one event at a time — batch requests aren't supported.

All App Events API endpoints follow this pattern: https://api.shopify.com/app/{version}/events.

The App Events API is versioned. To keep your app stable, make sure you specify a supported version in the URL. Learn more about API versioning.

The App Events API provides the following endpoint:

  • Create an event: Send events for monitoring, usage-based billing, or Shopify Analytics.

Learn more about App Events and building a billing event.


Anchor to Shopify Analytics eligibilityShopify Analytics eligibility

For an event to be available in Shopify Analytics, your app must be installed on the target shop and have a released analytics_app_events declaration. The event name and attributes must match that declaration and pass analytics validation.

You can declare Shopify-defined standard events or define custom events. Custom events that you send only for monitoring don't need a declaration, but they don't appear in Shopify Analytics. Use the exact declared name as event_handle:

Event kindDeclared name and API event_handle
Standardshopify.marketing.email_sent
Customexample-app.workflow_completed

Custom names use your app handle as the prefix. The shopify. prefix is reserved for standard events. The event. prefix used in ShopifyQL queries isn't part of the API handle.

Standard events use the registry's attributes and metrics. Your app can add custom attributes without redefining the standard ones. This standard event request includes the declared custom attribute processing_seconds:

{
"shop_id": "gid://shopify/Shop/23423423",
"event_handle": "shopify.marketing.email_sent",
"timestamp": "2026-10-02T14:30:00Z",
"idempotency_key": "email_message_55667788_sent",
"attributes": {
"campaign_id": 100472895,
"message_type": "campaign",
"processing_seconds": 1.8
}
}

For custom events, send the attributes that your app declares. This example matches the configuration in App Events in Analytics:

{
"shop_id": "gid://shopify/Shop/23423423",
"event_handle": "example-app.workflow_completed",
"timestamp": "2026-10-02T14:30:00Z",
"idempotency_key": "workflow_run_1042_completed",
"attributes": {
"workflow_type": "inventory_sync",
"items_processed": 12
}
}

Replace the example values with your app's handle and actual event data. Sending an undeclared event or attribute doesn't make it queryable in Shopify Analytics.


All events use a single request schema. Every event is logged and visible in the Dev Dashboard. If the event_handle matches a handle configured in a pricing plan as part of Shopify App Pricing, the event is also processed as a usage charge.

Caution

Don't include any data in attributes that, alone or in combination with other data, could identify an individual. This includes names, email addresses, phone numbers, and customer IDs.


shop_id•stringRequired

The ID of the merchant's shop that the event is associated with. For example, gid://shopify/Shop/23423423 or 23423423. Both a Shopify GID and a numeric shop ID (as a string) are accepted.


event_handle•stringRequired

The event name. If it matches a meter handle in your pricing configuration, then Shopify processes it for billing. All events are available for monitoring in the Dev Dashboard.

The name has a maximum of 128 characters and can only contain alphanumeric ASCII characters, underscores (_), periods (.), hyphens (-), and forward slashes (/).

For Shopify Analytics, use the exact name from your released declaration, such as shopify.marketing.email_sent or example-app.workflow_completed. The shopify. prefix is reserved for standard events.


timestamp•stringRequired

The ISO 8601 timestamp of when the event occurred (for example, 2026-01-27T14:30:00Z). The timestamp can't be more than 5 minutes in the future. Billing events outside the current cycle currently reject with RESULT_STATUS_FAILURE and a result_details message with details about the allowable window.


idempotency_key•stringRequired

An app-generated unique key to prevent duplicate events. Maximum 64 characters. The API enforces a 24-hour idempotency window — requests with a previously seen key within that window return the original response. For billing events, idempotency is enforced permanently.


attributes•objectRequired

A JSON object containing the event data. For billing events, the value field is required and specifies the quantity to apply to the meter. For details, see Understand the request fields. The attributes object has the following restrictions:

  • Maximum 15 keys in the attributes object
  • Keys are limited to 64 characters and can only contain alphanumeric ASCII characters, underscores (_), periods (.), and hyphens (-)
  • String values are limited to 128 characters (UTF-8)
  • Only scalar values are allowed (strings, numbers, booleans). Arrays and nested objects aren't supported.

For Analytics events, include required standard attributes and use values that match the declared types. Also include any custom attributes that your app declares with optional = false. Other custom attributes are optional.

For money attributes, send integer minor units in the event's declared currency. For example, send "offer_value": 1299 for USD 12.99. Currency comes from the event configuration, not the payload. Learn how to declare a currency for money attributes.


{} Request body

{
"shop_id": "gid://shopify/Shop/23423423",
"event_handle": "onboarding_completed",
"timestamp": "2026-01-27T14:30:00Z",
"idempotency_key": "onboard_23423423_v3",
"attributes": {
"onboarding_version": 3,
"steps_completed": 5
}
}
{
"shop_id": "gid://shopify/Shop/23423423",
"event_handle": "sms_sent",
"timestamp": "2026-01-27T14:30:00Z",
"idempotency_key": "evt_55667788",
"attributes": {
"value": 1
}
}

All successful requests return a 202 Accepted response with the following JSON body:


success•boolean

Whether the event was received successfully.


error•string

Present when success is false. A short explanation of what went wrong.


{} Response

{
"success": true
}

Anchor to Example: Report a billing eventExample: Report a billing event

This example shows the full flow of authenticating and sending a billing event.

POST

https://api.shopify.com/app/2027-01/events

// Step 1: Get an access token
const authResponse = await fetch("https://api.shopify.com/auth/access_token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: "{your_client_id}",
client_secret: "{your_client_secret}",
grant_type: "client_credentials",
}),
});
const { access_token } = await authResponse.json();

// Step 2: Send a billing event
const response = await fetch("https://api.shopify.com/app/2027-01/events", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${access_token}`,
},
body: JSON.stringify({
shop_id: "gid://shopify/Shop/23423423",
event_handle: "sms_sent",
timestamp: "2026-01-27T14:30:00Z",
idempotency_key: "evt_55667788",
attributes: {
value: 1,
},
}),
});

const data = await response.json();
// { "success": true }

Anchor to Status and error codesStatus and error codes

All API queries return HTTP status codes that can tell you more about the response.


202 Accepted

The event was received successfully. A 202 response means Shopify received the request — it doesn't confirm that billing validation passed. Billing errors are processed asynchronously and are only visible in the Dev Dashboard under Logs with the App Billing Event type filter. There are no synchronous billing error responses and no webhooks for billing validation failures. Replayed requests (same idempotency_key within the idempotency window) return the original response with an Idempotent-Replay: true header.

A 202 response also doesn't confirm acceptance for Shopify Analytics. Analytics validation is asynchronous. The event must match a released standard or custom declaration and use valid attributes. See Analytics troubleshooting.


400 Bad Request

The request parameters are invalid. The response includes { "success": false, "error": "Invalid request" }.


401 Unauthorized

The bearer token is missing or invalid.


403 Forbidden

The app isn't installed on the shop specified by shop_id, or the shop_id value is invalid.


409 Conflict

A request with the same idempotency_key is still being processed. Retry after a short delay.


429 Too Many Requests

Rate limit exceeded. The App Events API has a rate limit of 500 requests per second per app. Wait before retrying.


5xx Errors

An internal error occurred in Shopify. Check out the Shopify status page for more information.


{} Sample error responses

{
"error": "Unauthorized"
}
{
"success": false,
"error": "Rate limit exceeded"
}

By using the App Events API, you agree to the App Events API Terms of Service. Don't send personal information in event attributes, unless permitted in the App Events API documentation.


Was this page helpful?