Stripe invoice.created vs invoice.finalized: which webhook to listen for
Chooses between Stripe invoice.created and invoice.finalized webhooks. Use when wiring invoice automation and you need to know which event marks the actionable moment. Not for payment success handling.
TL;DR
invoice.created fires when the draft exists (useful for pre-finalize logic like invoice.upcoming warnings or custom validation); invoice.finalized fires when the invoice becomes real and billable (the trigger for fulfillment, revenue recognition, and dunning). Most post-billing automation belongs on invoice.finalized; use invoice.created only when you need the draft window. Listening to both without distinguishing them double-triggers flows.
Steps
- List what each automation needs: draft-time intervention vs post-finalize action.
Expected: Each listener gets a justified event.
- Put pre-finalize logic (warnings, validation, edits) on invoice.created.
Expected: Draft-window work runs before the invoice is real.
- Put fulfillment, accounting entries, and dunning on invoice.finalized.
Expected: Real-invoice work runs exactly once, on a real invoice.
- Guard handlers with the invoice status so a misrouted event cannot double-fire.
Expected: Defense in depth against duplicate processing.
- Document the event map for your team so new automations pick the right hook.
Expected: Future work inherits the correct pattern.
When to use
- You wire new invoice automation
- Handlers fire twice or at the wrong time
- You need draft-window vs finalized semantics
When not to use
- You handle payment outcomes (use invoice.payment_succeeded or failed)
- You need subscription lifecycle events (different webhook family)
- You only care about disputes (different family)
Compatibility
Stripe webhooks; invoice.created and invoice.finalized event types. All API versions with Billing.
Variant phrasings
### Stripe invoice.created vs finalized
### which invoice webhook to use Stripe
### invoice finalized webhook fulfillment
Root cause
The two events bracket the draft-to-real transition: created announces intent (the invoice might still change or never finalize), finalized announces commitment (number assigned, totals locked). Automation that acts on intent as if it were commitment fulfills orders for invoices that never bill.
Edge cases
- Manually finalized invoices fire finalized whenever you call it, which can be much later than created
- Voided drafts never fire finalized; handle the void path in your state machine
- Test clocks let you rehearse the full created-to-finalized sequence
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_eBWLsrkWHqXeZP2tFF67JQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.