All Docs
FeaturesCalmony PayUpdated March 15, 2026

Post-mortem: Checkout Session Expiry Parameter Mismatch

Post-mortem: Checkout Session Expiry Parameter Mismatch

Version: v1.0.108
Affected feature: Checkout Session creation (POST /api/v1/checkout/sessions)
Severity: Silent data loss — callers receive no error; session TTL is incorrect


Summary

A spec drift was identified between the Calmony Pay REST API and the SDK regarding how checkout session expiry is specified. The SDK accepts an expires_at Unix timestamp, while the REST API silently ignores expires_at and instead uses an expires_in integer (seconds). Callers relying on expires_at to control session lifetime received no error but got sessions with a default 30-minute TTL instead.


Background

The Calmony Pay REST API Contract (pinned spec) defines the checkout session creation payload as:

interface CreateCheckoutSessionParams {
  // ... other fields ...
  expires_at?: number; // Unix timestamp
}

The SDK client (src/lib/calmony-pay/client.ts) correctly exposes expires_at in its TypeScript types, consistent with the spec.


The Deviation

The REST API route handler at src/app/api/v1/checkout/sessions/route.ts uses a Zod schema (createCheckoutSessionSchema) that defines expiry as:

expires_in: z.number().int().min(300).max(86400).default(1800)
// seconds until expiry; range 300s–86400s; default 1800s (30 minutes)

The expires_at field is not present in this schema and is never read by the route handler. The API does not return a validation error when expires_at is passed — the field is simply stripped during parsing.

Behaviour comparison

Caller actionExpected behaviourActual behaviour
Pass expires_at: 1735000000 to REST APISession expires at Unix time 1735000000expires_at silently ignored; session expires in 30 minutes
Pass expires_in: 3600 to REST APISession expires in 1 hour✅ Works correctly
Pass expires_at via SDKForwarded to API, session expires at given timestampForwarded but ignored by API; 30-minute TTL applied

Impact

  • No error is surfaced to callers — this is a silent failure.
  • Sessions may expire earlier than intended (e.g. a session intended to last 2 hours expires in 30 minutes).
  • Callers using the SDK's TypeScript types had no indication anything was wrong.
  • Integrations built against the REST API directly using expires_in are not affected.

Immediate Guidance

Until a fix is released that aligns the REST API and SDK:

  1. If calling the REST API directly: Use expires_in (an integer number of seconds, between 300 and 86400) to control session TTL. Do not rely on expires_at.

    POST /api/v1/checkout/sessions
    {
      "expires_in": 7200
    }
    
  2. If using the SDK: Verify that your sessions are expiring at the expected time. If you have been passing expires_at, your sessions are currently using the default 30-minute TTL.

  3. Audit existing integrations: Any checkout flow with a session window longer than 30 minutes should be treated as potentially broken until the fix is confirmed deployed.


Next Steps

A follow-up release will resolve the mismatch by aligning the REST API schema and the SDK to a single consistent expiry parameter. The resolution will be documented in the changelog when shipped.


Affected Files

  • src/app/api/v1/checkout/sessions/route.ts — REST API route; uses expires_in
  • src/lib/calmony-pay/client.ts — SDK client; uses expires_at