Listen for the subscription_billing_attempts/failure webhook; the payload includes subscription_contract_id, error_code, and billing_attempt_id
Query the failed billing attempt with GET /admin/api/2025-07/subscription_billing_attempts/BILLING_ATTEMPT_ID.json to retrieve error_code and error_message
Trigger a new billing attempt after a delay using POST /admin/api/2025-07/subscription_billing_attempts.json with subscription_contract_id in the body
If the card is declined, send a payment update URL to the customer by generating a payment update URL via subscriptionContractUpdate with a new paymentMethodId, or redirect to the customer portal
After receiving subscription_billing_attempts/success webhook, confirm the resulting order_id is present in the payload and fulfill accordingly
Set a maximum retry count in your own logic — Shopify does not automatically retry failed billing attempts; your app owns the dunning cadence
Known gotchas
Shopify does not provide built-in dunning logic for subscription billing attempts — the Subscriptions API requires the app (Recharge, Loop, or custom) to implement all retry and notification logic
The billing attempt error_code field uses Shopify-specific codes (e.g., PAYMENT_METHOD_DECLINED, PAYMENT_METHOD_NOT_FOUND) not Stripe error codes — map them separately
Billing attempt creation is rate-limited under the standard Shopify API rate limits (REST: 2 req/s leaky bucket); use the X-Shopify-Shop-Api-Call-Limit response header to monitor usage
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp