Set up webhooks to track your app’s revenue and subscription events. This is important if you want Helium to display subscription, revenue, and other metrics!
This option is recommended if you are using StoreKit to handle your purchases but want to consume App Store Server Notifications on your own server and forward those events to Helium.
Grab the Webhook URL from the App Store Server Notifications section and use that value in your forwarding code.In your existing App Store Server Notifications handler, add a snippet that forwards the payload to Helium unchanged. Place it as early as possible — after you’ve parsed the incoming body, but before any type filtering or early returns — so that Helium receives every notification type, not just the subset your own handler acts on.Don’t couple your response to the forward. Await the forward and wrap it in a try/catch that only logs on failure. It should never throw or change the status you return to Apple — otherwise a hiccup forwarding to Helium would make Apple retry the whole notification and re-run your own handling.Forwarding is just a POST — send the request body to your Helium Webhook URL with Content-Type: application/json. The examples below cover a few common stacks, but any language works. Reach out to Helium support if you have any questions.
// Inside your existing app.post(...) ASSN handler, near the top:try { await axios.post( 'your-helium-webhook-url-here', req.body, { headers: { 'Content-Type': 'application/json' }, timeout: 5000 } );} catch (error) { console.error('Failed to forward webhook to Helium:', error.message);}
// Inside your existing POST handler, right after you read the body:// App Router: const body = await request.json();// Pages Router: const body = req.body;try { const response = await fetch( 'your-helium-webhook-url-here', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(5000) } ); if (!response.ok) { console.error(`Failed to forward webhook to Helium: status ${response.status}`); }} catch (error) { console.error('Failed to forward webhook to Helium:', error);}
// Inside your existing handler, after reading the request body into `body`// (a []byte holding the raw JSON payload), near the top:func() { ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodPost, "your-helium-webhook-url-here", bytes.NewReader(body)) if err != nil { log.Printf("Failed to forward webhook to Helium: %v", err) return } req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Printf("Failed to forward webhook to Helium: %v", err) return } defer resp.Body.Close() if resp.StatusCode >= 300 { log.Printf("Failed to forward webhook to Helium: status %d", resp.StatusCode) }}()
# Inside your existing route, near the top:try: response = requests.post( 'your-helium-webhook-url-here', json=request.json, headers={'Content-Type': 'application/json'}, timeout=5 ) response.raise_for_status()except requests.exceptions.RequestException as e: print(f'Failed to forward webhook to Helium: {e}')
# Inside your existing handler, right after you read the body:# body = await request.json()try: async with httpx.AsyncClient(timeout=5.0) as client: response = await client.post( 'your-helium-webhook-url-here', json=body, headers={'Content-Type': 'application/json'} ) response.raise_for_status()except httpx.HTTPError as e: print(f'Failed to forward webhook to Helium: {e}')