Skip to main content

Webhooks

AppActor sends real-time webhook notifications to your server when subscription events occur. Use webhooks to keep your backend in sync with purchase state.

How It Worksโ€‹

  1. Apple/Google send notifications to AppActor when subscription events occur
  2. AppActor processes the event, updates entitlements, and persists the event
  3. AppActor forwards a normalized webhook to your server

Inbound Webhooks (Store โ†’ AppActor)โ€‹

Apple Server Notifications V2โ€‹

Configure your Apple webhook URL in App Store Connect:

https://api.appactor.com/v1/webhooks/apple/:token

The :token is a unique per-app token generated when you create your app in AppActor. Apple sends signed JWS notifications that AppActor verifies before processing.

Verification:

  • JWS signature verification using Apple's root certificate
  • Bundle ID validation against your app configuration
  • Staleness protection โ€” notifications older than the configured max age are ignored

Google Cloud Pub/Subโ€‹

Configure a Pub/Sub subscription pointing to:

https://api.appactor.com/v1/webhooks/google/:appId

Google notifications are verified via OIDC Bearer token authentication.

Outbound Webhooks (AppActor โ†’ Your Server)โ€‹

Configure your webhook URL and secret in the AppActor dashboard. AppActor will send POST requests to your URL for every processed event.

Webhook Formatโ€‹

{
"eventId": "evt_abc123",
"type": "SUBSCRIPTION_RENEWED",
"appId": "app_xyz",
"data": {
"userId": "user_123",
"productId": "com.app.monthly",
"transactionId": "txn_456",
"originalTransactionId": "txn_001",
"expiresDate": "2025-02-15T00:00:00Z"
},
"timestamp": "2025-01-15T12:00:00Z"
}

Webhook Signatureโ€‹

Each webhook includes an HMAC-SHA256 signature in the X-AppActor-Signature header. Verify it using your webhook secret:

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

Event Typesโ€‹

EventDescription
INITIAL_PURCHASEFirst purchase of a subscription or product
SUBSCRIPTION_RENEWEDSubscription successfully renewed
SUBSCRIPTION_EXPIREDSubscription expired (not renewed)
SUBSCRIPTION_CANCELLEDUser cancelled auto-renewal
SUBSCRIPTION_REACTIVATEDUser re-enabled auto-renewal
SUBSCRIPTION_UPGRADEDUser upgraded to a higher tier
SUBSCRIPTION_DOWNGRADEDUser downgraded to a lower tier
SUBSCRIPTION_REVOKEDSubscription refunded by Apple/Google
GRACE_PERIOD_STARTEDPayment failed, grace period began
BILLING_RETRY_STARTEDApple/Google retrying failed payment
CONSUMPTION_REQUESTUser requested a refund (Apple)

Retry Logicโ€‹

If your server returns a non-2xx response, AppActor retries with exponential backoff:

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
4th retry30 minutes
5th retry2 hours

After 5 failed attempts, the webhook is marked as failed. You can view failed webhooks in the AppActor dashboard.

Best Practicesโ€‹

  1. Return 200 quickly โ€” Process webhooks asynchronously; return 200 immediately and handle the event in the background
  2. Handle duplicates โ€” Use the eventId to deduplicate; the same event may be delivered more than once
  3. Verify signatures โ€” Always verify the HMAC signature before processing
  4. Use HTTPS โ€” Webhook URLs must use HTTPS

Next Stepsโ€‹