Skip to content

Deadlines and expirations

Checkout sessions expire, card authorizations lapse, trials end, and invoices come due. Every clock the API runs is below, with the field that shows it, the event Flint sends when it passes, and what your integration should do.

How the API publishes a deadline#

A resource whose state changes at a time Flint knows publishes that time as an RFC 3339 timestamp in UTC. The field name tells you what happens at the deadline:

  • expires_at, or a qualified name such as recovery_expires_at, means the thing lapses by itself and stops being usable.
  • due_at, or a name such as evidence_due_at, means someone must act by then, and the resource continues either way. A deadline that is a calendar day is a _date field instead.

Read the timestamp from the resource instead of adding a duration to a creation time. Several lifetimes depend on your settings, the request, or a card network.

Three kinds of lapse, and request clocks#

A clock on a resource is one of three kinds, and the kind decides whether an event arrives. Each row's "When it passes" cell starts with its kind: "Status changes:", "Computed at read:", or "Act by:". A fourth label, "Request:", marks the clocks on requests and credentials.

  1. A lapse that changes the stored status. Flint sends that status's event, like any other transition. A checkout session that becomes expired sends checkout_session.expired.
  2. A lapse computed when you read. The stored status doesn't change, so Flint sends no event. An API key reads expired once expires_at has passed. Schedule your own check from the field, and read the resource before you act on it.
  3. An act-by deadline. Passing it changes nothing by itself. Flint sends a warning event before it only where you must act to avoid a loss, such as subscription.trial_ending and invoice.reminder_due.

Deadlines that a buyer acts on, such as a sign-in code, show up in Flint's emails and pages for the buyer, not in a webhook to you. The last section lists the request clocks, such as how long Flint waits for your webhook answer and how long it keeps an idempotency key. They aren't deadlines on a resource.

Checkout and payments#

What runs outHow longFieldWhen it passesWhat to do
Checkout session24 hours after creation by default. Set it per session with expires_in_seconds, or for every session with settings.checkout.default_expires_in_seconds.
CheckoutSession.expires_at
Status changes: The session becomes expired and Flint sends checkout_session.expired. A payment already in progress, such as a 3D Secure screen, can still finish (see payment recovery).Read expires_at instead of adding a duration to the creation time. Create a new session for a buyer who comes back late. Affirm needs at least 15 minutes left in an embedded session's payment window; inside the last 15 minutes the payment fails with PAYMENT_OPTION_UNAVAILABLE and reason action_window_too_short, so offer card.
Payment recovery after a session expiresUntil recovery_expires_at, at most an hour after the payment attempt itself expires.
CheckoutSession.recovery_expires_at
Computed at read: Recovery ends. Flint sends no event.Read the order with your API key to see whether the payment finished before you treat the session as lost.
Buyer step on a payment, such as 3D Secure30 minutes after the attempt starts.NoneStatus changes: Flint cancels the attempt's payments, the attempt ends canceled, and everything it held on the order is released. Each canceled payment sends payment_intent.canceled.The API doesn't show this deadline yet, so read the attempt's status. To release the order sooner, cancel the attempt.
Card authorization under manual captureSet by the card network, usually seven days for a card. Read it, never hardcode it.
PaymentIntent.authorization_expires_at
Status changes: The PaymentIntent moves to expired, the hold is released, and Flint sends payment_intent.expired. A capture sent afterward is refused with 409 ACTION_NOT_ALLOWED, and nothing is charged.Capture before authorization_expires_at, or cancel the hold. Store the timestamp with the order or booking the hold is for.
Refunds on Affirm payments120 days after the payment settles.NoneComputed at read: POST /v1/refunds returns 409 ACTION_NOT_ALLOWED with reason refund_unavailable. Flint sends no event.Refund within 120 days of settlement. After that, reimburse the buyer another way.
Card saved at checkout with a mobile phone numberThe buyer has 24 hours after the payment succeeded or was approved to confirm it.
CheckoutPaymentMethodSave.expires_at
Status changes: The card becomes failed, and no one can pay with it. Flint sends no event today.Wait for payment_method.activated before you charge a card saved this way.
Checkout sign-in code15 minutes for an emailed code. 10 minutes after the text provider first sends a texted code.
CheckoutSessionCustomerVerification.expires_at
Computed at read: The code stops working. Flint sends no event, because the buyer is the one who acts on it.Show the buyer a way to request a new code. A code also allows 5 tries, and a new request replaces the earlier emailed code.
Promotion codeUntil expires_at when you set one.
PromotionCode.expires_at
Computed at read: The code stops being redeemable. Flint sends no event.To extend the offer, update the code's expires_at.
Gift card reservation you created without capturing15 minutes by default, or until the expires_at you set, up to 24 hours.
GiftCardRedemption.expires_at
Status changes: The redemption becomes expired, the reserved value goes back to the card, and Flint sends gift_card_redemption.updated. A reservation held for a payment attempt follows that attempt instead.Capture the redemption when the sale succeeds, or cancel it when the sale fails.
Redirects from an archived custom domain30 days after you archive the domain, until redirect_expires_at.
CustomDomain.redirect_expires_at
Computed at read: Redirects from the hostname end, and links on it stop working. Flint sends no event, though archiving sent custom_domain.archived.Delete the records in dns_records once you no longer need the redirects, and replace the hostname in shared links, printed QR codes, and invoice links before the window ends.

Stock and delivery#

What runs outHow longFieldWhen it passesWhat to do
Inventory reservation hold15 minutes by default, and at most 24 hours from now.
InventoryReservation.expires_at
Status changes: Flint releases whatever is still held and sends inventory_reservation.hold_expired. Committed quantity stays.To give a buyer more time, send a new expires_at in a PATCH before the hold lapses. A lapsed hold can't be extended, so create a new reservation.
Delivery quote24 hours when every method uses fixed, tiered, or rate-table pricing. At most 15 minutes otherwise, and under 5 minutes for a cart with tracked inventory.
DeliveryQuote.expires_at
Computed at read: The quote is no longer valid. Flint sends no event.Quote again with the buyer's current cart and address, then select an option from the new quote.
Provisional delivery selectionUntil expires_at. Its calculated pricing is good until calculation_expires_at.
DeliverySelection.expires_at
DeliverySelection.calculation_expires_at
Computed at read: The selection can no longer be committed, and the calculated pricing is no longer valid. Flint sends no event.Select the option again before the buyer pays.
Choosing a delivery or pickup windowUntil the window's expires_at, the last moment the buyer can choose it. For a schedule_window method that is the preparation lead time before the window starts, or the same-day cutoff when that is earlier.
DeliveryWindowResource.expires_at
Computed at read: The buyer can no longer choose that window. Flint sends no event.Offer only windows whose expires_at is still ahead, and quote again for current windows.

Billing#

What runs outHow longFieldWhen it passesWhat to do
Invoice paymentThrough the invoice's due_date, set from its payment terms or a date you choose. It is overdue once that date ends in the invoice's time zone.
Invoice.due_date
Act by: Flint sends invoice.overdue while a balance remains, and is_overdue is true when you read the invoice. If the payment term carries a late fee, invoice.late_fee_due follows once the grace period ends. Before the due date, Flint sends invoice.reminder_due on your reminder cadence.When delivery_mode is caller_managed, send the reminder yourself on invoice.reminder_due. To charge a late fee, assess it after invoice.late_fee_due.
Subscription trialUntil trial_end_at.
Subscription.trial_end_at
Status changes: Flint charges the first period and the subscription stays trialing while the charge is pending. A successful charge makes it active and sends subscription.payment_succeeded and subscription.activated. A declined charge moves it straight to past_due, and the retry that succeeds sends subscription.activated once. Before the trial ends, Flint sends subscription.trial_ending.Grant paid access once subscription.activated arrives, and ask for a new card on subscription.past_due.
Subscription renewalAt next_billing_at.
Subscription.next_billing_at
Status changes: Flint creates the cycle's order and charges the saved payment method. Success sends subscription.payment_succeeded and order.paid for the order. A failed charge sends subscription.past_due. Before the renewal, Flint sends subscription.renewal_upcoming, three days ahead, or 30 days ahead for a subscription that renews every 12 months or more.Renew access on subscription.payment_succeeded, and keep next_billing_at for your own reminders.
Failed-payment retryAt next_retry_at. Flint retries on days 1, 4, 9, and 16 by default.
Subscription.next_retry_at
Status changes: Flint retries the charge. A retry that succeeds sends subscription.payment_succeeded and the subscription is active again. A failed automatic retry sends no event, and when every retry fails Flint sends subscription.dunning_exhausted and applies the end action.Ask the customer for a working card after subscription.past_due, and decide access when subscription.dunning_exhausted arrives.
Scheduled cancellationAt scheduled_cancel_at: the trial's end while trialing, the period's end while active, or next_retry_at while past_due.
Subscription.scheduled_cancel_at
Status changes: The subscription ends and Flint sends subscription.canceled. Scheduling it sent subscription.cancellation_scheduled.Revoke access on subscription.canceled, not at scheduled_cancel_at.
Scheduled resume after a pauseA pause with a length resumes at scheduled_resume_at, that many billing intervals after paused_at.
Subscription.scheduled_resume_at
Status changes: Flint applies the resume and sends subscription.resumed. The status can be past_due while an overdue period is collected.Grant paid access only when the status is active.
Scheduled invoice issueAt scheduled_issue_at, a future time you set on a draft.
Invoice.scheduled_issue_at
Status changes: Flint issues and emails the invoice and sends invoice.issued. If the issue fails, the invoice stays a draft, scheduled_issue_at is cleared, and Flint sends invoice.issue_failed.Set recipient_email on the draft, and watch for invoice.issue_failed.
Your Flint billing invoiceUntil the invoice's due_at. Processing pauses 21 days after the due date, or later if your billing terms allow more time, when the amount is still unpaid.
MerchantBillingInvoice.due_at
Act by: In live mode, Flint notifies your account owners, admins, and confirmed notification contacts one day after the due date, and again seven days before the pause. If the amount is still unpaid, processing pauses, payments and payouts read blocked with status_reason billing_suspended, and Flint sends merchant.readiness_updated.Keep enough in your Flint balance to cover what you owe, and read GET /v1/merchant-billing-balances for the amount.

Disputes#

What runs outHow longFieldWhen it passesWhat to do
Dispute evidenceUntil evidence_due_at. There is none when the dispute has no response window, such as a bank return.
Dispute.evidence_due_at
Act by: Once evidence_due_at has passed, reads show is_evidence_deadline_passed: true, is_action_required: false, and no accept in supported_actions. Webhook payloads keep the values from when Flint recorded the change. Flint sends no event.Respond or accept before evidence_due_at. Subscribe to dispute.needs_response to learn when action is required.

Files and downloads#

What runs outHow longFieldWhen it passesWhat to do
Upload URL15 minutes after the file is created.
FileUpload.expires_at
File.expires_at
Computed at read: The file's status reads expired and the signed upload URL stops working. Flint sends no event, because files send no webhooks.Upload within 15 minutes, or create a new file.
Unattached ready file24 hours after processing, or 7 days after its last detachment.
File.expires_at
Computed at read: The file's status reads expired. Flint sends no event, because files send no webhooks.Attach a ready file before expires_at, and upload it again if it lapsed.
Report download link24 hours after the report succeeds.
PublicDownload.expires_at
Computed at read: The download stops working. The report keeps its succeeded status, and Flint sends no event.Download the file soon after report.succeeded. To get it again, create a new report.

Credentials and sessions#

What runs outHow longFieldWhen it passesWhat to do
API key with an expiryUntil the expires_at you set. A key without one never expires.
APIKey.expires_at
Computed at read: The key stops authenticating and reads expired. Flint sends no event.Set a short expires_at for a contractor, a migration script, or a load test, and rotate other keys before they lapse.
Customer session secret and refresh tokenThe secret lasts 1 hour by default (5 minutes to 24 hours). The refresh token lasts 7 days by default (1 hour to 30 days), and refreshing doesn't extend it.
CustomerSession.expires_at
CustomerSession.refresh_token_expires_at
Computed at read: The secret stops authenticating account calls. When refresh_token_expires_at passes, the buyer signs in again. Flint sends no event.Refresh before expires_at. After refresh_token_expires_at, create a new session.
Onboarding email code15 minutes after it is issued.
OnboardingStartResult.expires_at
Computed at read: The verification_token stops working. Flint sends no event.Keep the token on your backend, and start email verification again for a new code.
Onboarding session24 hours, until onboarding_session_expires_at. It can't be refreshed.
OnboardingVerifyEmailResult.onboarding_session_expires_at
Computed at read: The onboarding session token stops working. Flint sends no event.Verify the email again to get a new session token.
First API key after verifying the emailWithin 5 minutes of verifying the email.NoneComputed at read: Issuing a key that holds accounts.api_keys.write returns 403 IDENTITY_VERIFICATION_REQUIRED. Flint sends no event.Issue the first key right after verifying, or verify the email again.
Merchant account session launch tokenUntil launch_token_expires_at.
MerchantAccountSession.launch_token_expires_at
Computed at read: The launch token stops working. Flint sends no event.Create a new merchant account session.
Onboarding requirements deadlineUntil due_at on the merchant's readiness requirements.
ReadinessRequirements.due_at
ResponseWarning.due_at
Act by: Information still due moves to past_due, and payments readiness reads blocked with status_reason requirements_past_due. Readiness changes send merchant.readiness_updated.When merchant.readiness_updated arrives, read GET /v1/merchant and follow the next actions it lists.
Previous signing secret after a rotation24 hours by default. For a webhook endpoint, set expire_previous_secret_at up to 7 days out.
WebhookEndpoint.previous_secret_expires_at
PartnerApp.previous_secret_expires_at
Computed at read: The replaced secret stops signing, and deliveries carry one signature from the new secret. Flint sends no event.Deploy the new secret before previous_secret_expires_at, then remove the old one.
API version rollback offer72 hours after you move to a newer version.
Merchant.api_version_rollback_expires_at
WebhookEndpoint.api_version_rollback_expires_at
PartnerApp.api_version_rollback_expires_at
Computed at read: api_version_previous clears and the rollback offer ends. You can still set any supported version. Flint sends no event.To roll back, send the api_version_previous value as api_version before the offer ends.
Recovering a new gift card code24 hours after the card is created or its code is rotated.
CreateGiftCardResult.code_recovery_expires_at
RotateGiftCardCodeResult.code_recovery_expires_at
Computed at read: A replay with the same Idempotency-Key stops returning the code. Flint sends no event.Store the code as a credential when you create or rotate it.

Webhooks and requests#

What runs outHow longFieldWhen it passesWhat to do
Answering a webhook delivery10 seconds per attempt.NoneRequest: The attempt fails as webhook_timeout, and Flint retries on its schedule.Return a 2xx within 10 seconds and do the real work in a background job.
Webhook delivery retriesUp to 9 automatic attempts over about 3 days. The ninth lands 71 to 86 hours after the first.
WebhookDelivery.next_retry_at
Request: Flint makes the next attempt. After the ninth failed attempt the delivery is failed, and you can resend it.Deduplicate deliveries before you process them, and resend failed ones after you fix the endpoint.
Delivery JWT5 minutes: exp is iat plus 300 seconds.NoneRequest: A token past exp fails verification.Check iat and exp with at most 60 seconds of leeway, and reject a token whose lifetime exceeds 300 seconds.
Event payload retention14 days. The records themselves are deleted after 90 days.NoneRequest: The event keeps its metadata and drops data. A pending delivery stops retrying, and a resend returns 409 ACTION_NOT_ALLOWED with reason payload_expired.Read the resource with GET for its current state.
Idempotency key24 hours from the original request on routes where the key is optional. On routes that require it, as long as the command's effects exist, except POST /v1/customer-sessions/refresh, which holds it for 24 hours.NoneRequest: On an optional route, the same key runs as a new request. A required route returns the original result however late the retry arrives.Keep automated retries well inside 24 hours, and never reuse an old key for a new action.
Rate limitWait the number of seconds in the Retry-After header.NoneRequest: The next request can succeed. A 429 RATE_LIMIT_EXCEEDED means the request didn't run.Wait that long, then send the same request with the same Idempotency-Key.
Gift card challenge proof5 minutes after it is issued, for one checkout session and one use.NoneRequest: Flint rejects the apply request with GIFT_CARD_CHALLENGE_REQUIRED and reason proof_rejected.Remount the challenge frame for a new proof before each apply request while a challenge is required.
Command line sign-inAccess tokens last 15 minutes and refresh on their own. A session ends after 30 days without a refresh, or 90 days after sign-in, whichever comes first.
OAuthTokenResponse.expires_in
Request: The session ends, and the CLI needs a new sign-in.For unattended jobs, use an API key instead of a signed-in session.

Next steps#

WebhooksDelivery, retries, and deduplication.Webhook events catalogEvery event and when it fires.Statuses and lifecyclesWhat each status means and which actions are allowed next.IdempotencySend a key on every write and reuse it when you retry.

Was this helpful?