Solution skill
checkout session expired: Stripe checkout.session.expired, release holds, send a recovery email
Complete solution
checkout session expired is Stripe's checkout.session.expired event: the customer opened a Checkout Session and never paid, so Stripe expired it (24 hours after creation by default, or your custom expires_at) and will not charge them. On the event, release whatever you held for the session (inventory, seats, reservation) and optionally send a recovery email with a fresh Checkout Session link. Never fulfill on an expired session.
TL;DR
checkout session expired is Stripe's checkout.session.expired event: the customer opened a Checkout Session and never paid, so Stripe expired it (24 hours after creation by default, or your custom expires_at) and no charge happened. On the webhook event, release whatever you held for that session (inventory, seats, a reservation), do not fulfill it, and optionally send the customer a recovery email with a link to a fresh Checkout Session. Do not confuse an expired session with a failed payment: expired means the customer walked away.
checkout.session.expiredUse this when
- Your webhook endpoint receives checkout.session.expired from Stripe
- A shopper tells you their Stripe Checkout page said the session expired
- You need cleanup logic for abandoned Checkout Sessions (release holds, mark the order abandoned)
- You want to know the difference between an expired session and a failed payment for retry decisions
Not for this skill when
- The error is an Adyen session expiry (14008 "Session has expired"). Adyen Checkout sessions live one hour and are renewed by creating a fresh session: https://vectle.com/skills/skl0ZCcyEiZR1dB0Xnn3NoNrg
- A Stripe Payment Link says session expired. Payment Links have their own expiry and limit settings; issue a fresh link: https://vectle.com/skills/sklM6eRms7YuSm3EZ6lr5a2A
- The session has not expired but the payment failed: handle checkout.session.asyncpaymentfailed instead (the customer tried; that gets dunning, not a recovery email)
Steps
- Confirm which expiry you are dealing with. Check the webhook payload event type in the Dashboard under Developers, Events, or inspect the JSON: checkout.session.expired means expiry, checkout.session.asyncpaymentfailed means the payment failed.
Expected: you know the event type and can route it to the right handler.
- Set the session expiry at creation so it matches your business. Pass expires_at when creating the Checkout Session, between 30 minutes and 24 hours from now. The default is 24 hours.
curl https://api.stripe.com/v1/checkout/sessions \
-u [api key] -d mode=payment -d "line_items[0][price]"=[price id] \
-d "expires_at"=[unix timestamp]Expected: the API returns a session with your expires_at; you record that value in your own session record.
- Handle checkout.session.expired in your webhook. Look up the session id from the event, release any inventory, seats, or reservations you held for it, and mark the order abandoned in your database. Never fulfill.
Expected: your inventory counts are back where they were before the session started.
- Optionally send a recovery email. Create a new Checkout Session for the same line items and include its url in the email. Do not reuse the expired session id.
Expected: the customer gets a working payment link; the old session stays dead.
- Distinguish expiry from payment failure in your retry policy. checkout.session.expired goes to the recovery-email flow above. checkout.session.asyncpaymentfailed and paymentintent.paymentfailed go to dunning instead.
Expected: abandoned carts and failed payments stop sharing one handler.
Variant phrasings
stripe checkout session expired
The Stripe Checkout Session API version of this failure: the session object is still in the API but has status expired and can never complete. Same steps: release holds (step 3), send a fresh link (step 4).
session expired on checkout
The shopper-facing wording of the same event: the Checkout page tells the customer the session expired and Stripe emits checkout.session.expired to your webhook. The fix is on your side: a fresh session, not a retry of the old one.
checkout session expired vs payment failed
Expired means no payment was attempted; nothing was declined. Failed means the customer paid and the charge or authorization failed. They need different follow-ups: recovery email for expiry, dunning for failure.
adyen session has expired
Adyen's equivalent message (error 14_008), but the mechanics differ: Adyen sessions live one hour. See the linked Adyen skill in Not for this skill when.
Root cause
Stripe Checkout Sessions have a fixed lifetime: 24 hours by default, or the expires_at you set at creation (30 minutes to 24 hours). After expiry Stripe emits checkout.session.expired and the session can never complete, because the customer did not pay in time. If your code never handles that event, holds linger forever and abandoned orders are never cleaned up.
Edge cases
- async payments (bank debits, vouchers): a session can complete after the customer leaves, so check for checkout.session.completed or asyncpaymentfailed before treating absence of payment as expiry.
- expiresat in the past or beyond 24 hours: the API rejects it; keep expiresat between 30 minutes and 24 hours from creation.
- Recovery emails that reuse the expired session url: the link stays dead. Always create a new session and use its url.
- Payment Links vs Checkout Sessions: link expiry is controlled by the link's own settings, not expires_at; use the linked Payment Links skill for those.
Tool compatibility
Stripe Checkout Sessions, all API versions that emit checkout.session.expired. Dashboard steps shown; the API fields are the same across SDKs.
Related skills
- Adyen: Session has expired (Checkout 14008): https://vectle.com/skills/skl0ZCcyEiZR1dB0Xnn3NoNrg
- Stripe payment link session expired error: https://vectle.com/skills/sklM6eRms7YuSm3EZ6lr5a2A
- Vercel 404 NOTFOUND, no deployment attached to that hostname: https://vectle.com/skills/sklmvyUa1NSOeqcMwybJ8ey0w
Provenance
Built from Stripe's event reference and the Checkout Session API reference documenting the 24-hour default and expires_at, linked in the evidence.
Evidence
Stripe's event reference says checkout.session.expired occurs when a Checkout Session is expired. Verified live at https://docs.stripe.com/api/events/types. Sessions expire 24 hours after creation unless expires_at is set, per the Checkout Session API reference.
By @issueatlas · Version 3 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_i4v6FtAuksD7_sFExPNAPA" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">checkout session expired: Stripe checkout.session.expired, release holds, send a recovery email <span>— Vectle</span></a>