Developer Docs
Webhook API
Configuration

Webhook Configuration

Joy Subscription delivers outbound webhooks (Joy server → your endpoint). Before any event can be delivered, you must configure a receiver on the integration / external-app settings for your shop. Configuration lives under the Developers tab in the Shopify admin of the app.

Each external app is configured with three values: a receiver URL (address), an apiKey that identifies the integration, and an apiSecret used to sign every payload. Joy will not deliver events until a signing secret has been generated, because every delivery is signed with HMAC-SHA256.

Configuration fields

Webhook delivery is configured per shop. The external app is configured with the following fields:

FieldMaps to headerRequiredDescription
addressYesYour receiver URL. Joy sends every delivery to this endpoint as an HTTP POST with a JSON body. Must be HTTPS.
apiKeyX-API-KeyYesIdentifies the integration. Sent on every delivery so your endpoint can recognize the caller.
apiSecretX-Signature (used to sign)YesThe signing secret. Joy computes base64(HMAC_SHA256(apiSecret, JSON.stringify(payload))) and sends it in the X-Signature header. A secret is required for Joy to send deliveries.

Note: The apiSecret is server-side only. Never embed it in browser JavaScript, mobile apps, or any client the customer can inspect. Verify signatures only on your backend.

Steps

  1. Open your app's Developers tab in the Shopify admin.
  2. Set the endpoint URL (address) — the HTTPS receiver where Joy will POST deliveries.
  3. Generate a signing secret (apiSecret). This is required for Joy to send deliveries. Store it in your server-side secret manager.
  4. Set the apiKey that identifies your integration. It is sent on every delivery in the X-API-Key header.
  5. Choose the topics you want delivered to this endpoint (see the topic list below).
  6. Save the configuration.

After saving, Joy starts delivering the selected topics to your address. Each delivery is signed with the apiSecret, carries an idempotency key for deduplication, and is retried on retryable failures.

Endpoint URL requirements

  • The address must be reachable over HTTPS (TLS). Plain HTTP is not supported.
  • Your endpoint must respond with a 2xx status as fast as possible, then process the event asynchronously.
  • Joy sends an HTTP POST with Content-Type: application/json.

A configured receiver receives every delivery with these headers:

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

Signing secret

A signing secret (apiSecret) is mandatory — Joy will not deliver events until one is generated. Joy uses it to sign every payload:

X-Signature = base64( HMAC_SHA256( apiSecret, JSON.stringify(payload) ) )

payload is the full JSON request body, including the delivery fields timestamp and idempotencyKey. To verify, re-serialize the parsed JSON body and recompute the HMAC, then compare with a timing-safe comparison.

const crypto = require('crypto');
 
function verifyJoySignature(rawBody, signatureHeader, apiSecret) {
  // rawBody: the exact JSON string received (or JSON.stringify of the parsed body).
  const expected = crypto
    .createHmac('sha256', apiSecret)
    .update(rawBody)
    .digest('base64');
 
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Note: If your signing secret is ever exposed, rotate it from the Developers tab and update your receiver. The new secret takes effect for subsequent deliveries.

Topics

Select the topics you want delivered to your endpoint. Topic names match Shopify's native webhook topic strings (trial topics are custom to Joy):

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.
subscription_billing_attempts/successA billing attempt succeeds.
subscription_billing_attempts/failureA billing attempt fails.
orders/createA subscription-related order is created.
orders/updateA subscription-related order is updated.
orders/cancelledA subscription-related order is cancelled.
trial/endingA trial is approaching its end (emitted by the trial cron).
trial/endedA trial has ended (emitted by the trial cron).

Notes:

  • trial/ending and trial/ended are custom topics produced by Joy's trial cron — Shopify has no native equivalent.
  • The orders/* topics only fire for subscription-related orders, identified by a Joy subscription note (such as Subscription #123 cycle 4) or by a selling-plan line item. Non-subscription orders are not forwarded.
  • The public topic name is sent in the X-Shopify-Topic header.

Delivery behavior

Once a topic is configured, deliveries follow this model:

  • Transport: HTTP POST with a JSON body to your configured address.
  • Acknowledgement: respond with a 2xx status as fast as possible, then do the real work 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: every delivery carries an idempotency key (header X-Idempotency-Key and idempotencyKey in the body). Retries reuse the same key — deduplicate on it.
Product
Install on ShopifyWebsitePricing
Resources
DocumentationGetting StartedFAQsIntegrations
Company
Avada GroupContact Support
© 2026 Avada Group. All rights reserved.