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โ
- Apple/Google send notifications to AppActor when subscription events occur
- AppActor processes the event, updates entitlements, and persists the event
- 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โ
| Event | Description |
|---|---|
INITIAL_PURCHASE | First purchase of a subscription or product |
SUBSCRIPTION_RENEWED | Subscription successfully renewed |
SUBSCRIPTION_EXPIRED | Subscription expired (not renewed) |
SUBSCRIPTION_CANCELLED | User cancelled auto-renewal |
SUBSCRIPTION_REACTIVATED | User re-enabled auto-renewal |
SUBSCRIPTION_UPGRADED | User upgraded to a higher tier |
SUBSCRIPTION_DOWNGRADED | User downgraded to a lower tier |
SUBSCRIPTION_REVOKED | Subscription refunded by Apple/Google |
GRACE_PERIOD_STARTED | Payment failed, grace period began |
BILLING_RETRY_STARTED | Apple/Google retrying failed payment |
CONSUMPTION_REQUEST | User requested a refund (Apple) |
Retry Logicโ
If your server returns a non-2xx response, AppActor retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 10 minutes |
| 4th retry | 30 minutes |
| 5th retry | 2 hours |
After 5 failed attempts, the webhook is marked as failed. You can view failed webhooks in the AppActor dashboard.
Best Practicesโ
- Return 200 quickly โ Process webhooks asynchronously; return 200 immediately and handle the event in the background
- Handle duplicates โ Use the
eventIdto deduplicate; the same event may be delivered more than once - Verify signatures โ Always verify the HMAC signature before processing
- Use HTTPS โ Webhook URLs must use HTTPS
Next Stepsโ
- API Authentication โ Understand API key types
- API Reference โ Full REST API documentation