Webhooks overview
Outgoing webhooks for slideshow events, plus the Stripe billing events we consume.
Register HTTPS endpoints from /integrations/webhooks (UI) or POST /v0/api/webhooks (API). Each delivery is HMAC-SHA256 signed with your per-endpoint secret and carries the X-ViralSlides-Signature, X-ViralSlides-Event, and X-ViralSlides-Delivery-Id headers. Failed deliveries retry on a 1m / 5m / 30m / 2h / 12h backoff for up to six attempts.
Available events
- slideshow.completed — generation finished; slides + marketing assets are ready.
- slideshow.failed — generation failed (payload includes the error message).
- quota.exceeded — the org hit its monthly slideshow / hook-refresh / apps quota.
{
"slideshow_id": "ss_01HQ...",
"organization_id": "org_01HQ...",
"app_id": "app_01HQ...",
"app_name": "Tidy",
"hook_text": "POV: your app feed but tidier",
"language": "en",
"slide_count": 6,
"template_set": "default"
}Verifying signatures
Compute HMAC-SHA256 of the raw request body using your endpoint secret and compare against the hex digest in X-ViralSlides-Signature (formatted as `sha256=<hex>`). Reject the request if the signatures don't match in constant time.
Stripe events we consume
Our Stripe webhook endpoint (https://api.viralslides.app/v0/public/billing/stripe-webhook) handles the events below. Each is signed and verified via STRIPE_WEBHOOK_SECRET; duplicates are deduped by event id.
| Event | Effect |
|---|---|
| customer.subscription.created | Creates a local subscription and mirrors plan + status onto the organization. |
| customer.subscription.updated | Upserts the subscription. Status maps to ACTIVE / TRIALING / PAST_DUE / CANCELED. |
| customer.subscription.deleted | Marks the subscription CANCELED and suspends the organization. Triggers a notification email. |
| invoice.payment_failed | Sets the org status to PAST_DUE and emails the owner the amount due. |
| invoice.payment_succeeded | Reactivates the org (ACTIVE) and rolls over usage_counters for the new billing period. |
| checkout.session.completed | Captures stripe_customer_id on the organization for fast linkage of the first checkout. |
Idempotency
Every Stripe event id is recorded in processed_webhook_events on first delivery and short-circuited on subsequent retries. This means it's safe for Stripe to redeliver events — we'll just acknowledge with received: true, duplicate: true.
Share & oEmbed
Public share links are issued per slideshow from the Slideshows page or POST /v0/api/slideshows/:id/share. Tokens are 32-char base64url, unguessable, and can be rotated or password-protected. Share URLs follow the pattern https://viralslides.app/share/<token>; the same token is valid in the iframe-safe embed at /embed/<token>. oEmbed discovery is exposed at GET /v0/public/oembed?url=<share-url>&format=json.
Slack & Discord notifications
Add incoming webhook URLs from Slack or Discord at /integrations/notify. ViralSlides will format slideshow.completed / slideshow.failed / quota.exceeded events into native Slack attachments or Discord embeds and POST them to your channel. Test-send from the UI before relying on the channel in production.