Webhooks
How OrbitKit uses Stripe webhooks internally and how to monitor subscription changes via polling.
OrbitKit uses Stripe webhooks to keep subscription state synchronized. These webhooks are internal (Stripe sends them to OrbitKit’s servers) — they are not user-facing webhooks that you subscribe to.
Stripe webhook events
OrbitKit listens for the following Stripe events:
| Event | What it triggers |
|---|---|
checkout.session.completed |
Creates the subscription record, links Stripe customer to user |
customer.subscription.updated |
Updates plan type, status, and billing period |
customer.subscription.deleted |
Marks subscription as canceled, sets TTL for site teardown |
invoice.paid |
Confirms payment, extends billing period |
invoice.payment_failed |
Marks subscription as past due, triggers retry logic |
What happens on each event
checkout.session.completed
When a user completes Stripe Checkout:
- The subscription is created in the database with
status: active - The Stripe customer ID is linked to the user ID
- The app’s site becomes eligible for deployment
customer.subscription.updated
Fires when a subscription changes (plan switch, renewal, etc.):
- Updates
planType(monthly/yearly) andstatusin the database - Updates
currentPeriodEndtimestamp
customer.subscription.deleted
When a subscription is fully canceled (past the grace period):
- Sets
status: canceledin the database - Sets a TTL timestamp — the site will show a placeholder after this date
- Releases any custom domain associated with the app
invoice.paid / invoice.payment_failed
Payment confirmations and failures update the subscription status accordingly. Failed payments trigger Stripe’s automatic retry schedule.
Monitoring subscription changes
Since OrbitKit doesn’t offer user-facing webhooks, use polling to detect changes:
# Check account status periodically
curl -H "Authorization: Bearer $TOKEN" \
https://api.orbitkit.io/api/account
let url = URL(string: "https://api.orbitkit.io/api/account")!
var request = URLRequest(url: url)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let account = try JSONDecoder().decode(Account.self, from: data)
// Check account.subscriptions for status changes
const res = await fetch("https://api.orbitkit.io/api/account", {
headers: { Authorization: `Bearer ${token}` },
});
const { subscriptions } = await res.json();
// Check each subscription's status, planType, currentPeriodEnd
The subscriptions array in the response includes each app’s current status, planType, and currentPeriodEnd.
Recommended polling strategy
- After a checkout or plan change: poll every 5 seconds for up to 30 seconds
- For general monitoring: poll every 5–15 minutes
- Use exponential backoff if you receive errors
See also
- Subscriptions & Billing — All subscription management endpoints
- Best Practices — Polling patterns and token management