quotastack Docs
Docs / API / Events / subscription.renewal_due

subscription.renewal_due

Fired N days before a prepaid subscription's period_end (default 3).

When it fires

QuotaStack sends this before a manual prepaid period ends, once per cycle. The lead time is your renewal_due_days. The event is your cue to take the money and call renew. A QuotaStack job raises it. No API call does.

When it does not fire

Postpaid subscriptions roll over on their own, so QuotaStack sends no due event. Payment connectors do the same. The provider plans the charge, and the public renew call returns 409 Conflict. QuotaStack sends at most one due event per cycle.

data
FieldTypeMeaning
idrequiredstring (uuid)
tenant_idrequiredstring (uuid)
customer_idrequiredstring (uuid)
plan_variant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

statusrequiredstring
  • trialing
  • active
  • cancelling
  • canceled
  • expired
  • paused
  • overdue
  • contract_ended
originoptionalstring
  • api
  • import

How the subscription was created — api (normal create) or import (legacy cutover).

started_atrequiredstring (date-time)
trial_ends_atoptionalstring (date-time)
current_period_startoptionalstring (date-time)
current_period_endoptionalstring (date-time)
billing_anchoroptionalinteger

Day of month (1-28) for monthly renewals.

cancel_at_period_endrequiredboolean
scheduled_variant_idoptionalstring (uuid)
external_subscription_idoptionalstring
paused_atoptionalstring (date-time)
resumed_atoptionalstring (date-time)
expired_atoptionalstring (date-time)
contract_startoptionalstring (date-time)
contract_endoptionalstring (date-time)
contract_ending_soon_daysrequiredinteger
metadatarequiredobject
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Example payload
{
  "event_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a51",
  "event_type": "subscription.renewal_due",
  "tenant_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a01",
  "environment": "live",
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_customer_id": "user_42",
  "created_at": "2026-07-29T00:00:00Z",
  "idempotency_key": "sub-renewal-due:0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a21",
  "data": {
    "subscription_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a21",
    "current_period_end": "2026-08-01T00:00:00Z",
    "plan_variant_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a12"
  }
}

What to do

Charge the customer through your own payment provider. Then call renew, which grants the new credits and sends subscription.renewed. Skip it and the subscription goes overdue, then runs out.

Which calls fire this

  • QuotaStack's scheduler, on a timer — no API call involved.

See also

Delivery

QuotaStack guarantees at-least-once delivery. An event may be delivered more than once if your endpoint returns a non-2xx response, the connection fails, or the request exceeds the delivery timeout.

Delivery timeout: 5 seconds per attempt. If your endpoint does not return a 2xx within 5 seconds, the attempt is treated as a failure and retried. Not configurable today.

One webhook URL per tenant. Multiple URLs and per-event routing are not supported. Configure the URL via the tenant config endpoint.

Retry schedule

If delivery fails (non-2xx response, timeout, or network error), QuotaStack retries with exponential backoff:

AttemptDelay after previous
1Immediate
230 seconds
35 minutes
430 minutes
52 hours
68 hours
724 hours

After 7 failed attempts, the event is moved to a dead letter queue. Dead-lettered events are not lost — you can requeue them yourself, from the dashboard (Activity → Webhooks → Redeliver) or the API:

curl -X POST https://api.quotastack.io/v1/webhooks/events/{event_id}/redeliver \
  -H "X-API-Key: $QS_KEY" \
  -H "Idempotency-Key: redeliver:{event_id}"

Redelivery resets the event to pending with a fresh retry schedule (7 new attempts). The next attempt signs with your current secret — useful when the event dead-lettered because of a secret rotation or an endpoint outage you have since fixed. Only dead_letter events can be redelivered; the call returns 409 for events in any other status.

Handling duplicates

Because delivery is at-least-once, your webhook handler should be idempotent. Use the webhook-id header for deduplication — if you have already processed an event with that ID, return 200 and skip processing.