Developer Docs
Webhook API
Webhook Events

Webhook Events

Joy Subscription delivers outbound webhooks (Joy server → your endpoint) for subscription lifecycle, billing, order, and trial events. This page is the reference for the native event topics, their triggers, and the shared payload envelope.

Topic names match Shopify's native webhook topic strings — except the two trial topics, which are custom to Joy. The public topic name is sent in the X-Shopify-Topic header on every delivery.

Native topics

Subscription contracts

TopicTrigger
subscription_contracts/createA subscription contract is created.
subscription_contracts/updateA subscription contract is updated.
subscription_contracts/activateA contract is activated.
subscription_contracts/pauseA contract is paused.
subscription_contracts/resumeA paused contract is resumed.
subscription_contracts/cancelA contract is cancelled.
subscription_contracts/expireA contract reaches its end / expires.

Billing attempts

TopicTrigger
subscription_billing_attempts/successA billing attempt succeeds.
subscription_billing_attempts/failureA billing attempt fails.

Orders

TopicTrigger
orders/createA subscription-related order is created.
orders/updateA subscription-related order is updated.
orders/cancelledA subscription-related order is cancelled.

orders/* topics only fire for subscription-related orders, identified by a Joy subscription note like Subscription #123 cycle 4, or by a selling-plan line item. Non-subscription orders are not forwarded.

Trial (custom)

TopicTrigger
trial/endingA trial is approaching its end (emitted by the trial cron).
trial/endedA trial has ended (emitted by the trial cron).

trial/ending and trial/ended are custom topics produced by Joy's trial cron. Shopify has no native equivalent.

Payload envelope

All topics share one consistent payload shape. The body is the topic-specific event payload, augmented by Joy with two delivery fields:

FieldTypeDescription
timestampstring (ISO 8601)When the payload was built. Mirrors the X-Timestamp header.
idempotencyKeystringMirrors the X-Idempotency-Key header. Default form: <topic>-<uniqueId>-<shopDomain>.
(event fields)objectTopic-specific fields. See the examples below.

The HMAC signature in X-Signature is computed over the entire JSON body, including timestamp and idempotencyKey. Verify against the exact received bytes (or a re-serialized copy of the parsed JSON) before trusting any payload.

Examples

subscription_contracts/cancel

{
  "subscriptionContractId": 123456789,
  "status": "CANCELLED",
  "reasonCode": "too_expensive",
  "timestamp": "2026-06-24T10:00:00.000Z",
  "idempotencyKey": "subscription_contracts/cancel-abc123-example.myshopify.com"
}

subscription_billing_attempts/failure

{
  "subscriptionContractId": 123456789,
  "billingAttemptId": "…",
  "errorCode": "…",
  "timestamp": "2026-06-24T10:05:00.000Z",
  "idempotencyKey": "subscription_billing_attempts/failure-def456-example.myshopify.com"
}

orders/create

The orders/* payloads are projected to the documented order shape:

{
  "orderId": 5544332211,
  "subscriptionContractId": 123456789,
  "cycleIndex": 4,
  "totalPrice": "42.00",
  "currency": "USD",
  "timestamp": "2026-06-24T10:10:00.000Z",
  "idempotencyKey": "orders/create-ghi789-example.myshopify.com"
}

For orders/* events, both subscriptionContractId and cycleIndex may be null when the order is subscription-related via a selling-plan line item but carries no Joy subscription note to parse. Do not assume these fields are always present.

Request headers

Every delivery carries the headers below. Filter on X-Shopify-Topic and ignore topics you don't handle.

HeaderDescription
Content-Typeapplication/json.
X-API-KeyYour configured apiKey.
X-SignatureBase64 HMAC-SHA256 of the JSON body, keyed with your apiSecret.
X-Shopify-TopicThe Shopify-native topic name (e.g. subscription_contracts/cancel).
X-Shopify-DomainThe shop domain (e.g. example.myshopify.com).
X-Idempotency-KeyUnique key for this event; identical across retries.
X-TimestampISO 8601 timestamp of when the payload was built.

Delivery and retries

  • Transport: HTTP POST with a JSON body to your configured address.
  • Acknowledgement: respond with a 2xx status as fast as possible, then process asynchronously.
  • Retries: on a retryable failure Joy makes up to 2 retries (3 attempts total). The base retry delay is 3000 ms, doubling each attempt (retryDelay * 2^(attempt-1) → roughly 3s then 6s).
  • Retryable conditions: HTTP status >= 500, 429, or 408. Other 4xx responses are treated as non-retryable and the delivery is dropped after logging.
  • Idempotency: retries reuse the same idempotencyKey. Deduplicate on it so each event is processed at most once.
Product
Install on ShopifyWebsitePricing
Resources
DocumentationGetting StartedFAQsIntegrations
Company
Avada GroupContact Support
© 2026 Avada Group. All rights reserved.