Documentation

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:

  1. The subscription is created in the database with status: active
  2. The Stripe customer ID is linked to the user ID
  3. The app’s site becomes eligible for deployment

customer.subscription.updated

Fires when a subscription changes (plan switch, renewal, etc.):

  1. Updates planType (monthly/yearly) and status in the database
  2. Updates currentPeriodEnd timestamp

customer.subscription.deleted

When a subscription is fully canceled (past the grace period):

  1. Sets status: canceled in the database
  2. Sets a TTL timestamp — the site will show a placeholder after this date
  3. 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.

  • 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