All Docs
FeaturesCalmony PayUpdated March 15, 2026

Spec Drift: Checkout Session Expiry Field Mismatch (v1.0.106)

Spec Drift: Checkout Session Expiry Field Mismatch

Version: v1.0.106
Severity: Medium
Status: Identified — fix in pipeline

Summary

A divergence was found between the Calmony Pay SDK contract and the REST API implementation for checkout session expiry. The SDK documents expires_at (a Unix timestamp), while the REST API silently accepts and processes only expires_in (a duration in seconds). Callers sending expires_at to the REST API receive no error — the parameter is simply ignored and a default 30-minute session TTL is applied.


Background

When creating a checkout session, integrators need control over how long a session remains valid. The pinned API contract specifies this via an expires_at field — an absolute Unix timestamp indicating when the session should expire.

The SDK client correctly exposes this field:

// src/lib/calmony-pay/client.ts
interface CreateCheckoutSessionParams {
  // ...
  expires_at?: number; // Unix timestamp
}

However, the REST API endpoint uses a different schema:

// src/app/api/v1/checkout/sessions/route.ts
const createCheckoutSessionSchema = z.object({
  // ...
  expires_in: z.number().int().min(300).max(86400).default(1800), // seconds
});

expires_in is an integer number of seconds (minimum 5 minutes, maximum 24 hours, defaulting to 30 minutes). The API never reads an expires_at parameter from the request body.


Impact

ScenarioExpected behaviourActual behaviour
Caller sends expires_at (Unix timestamp) to REST APISession expires at the specified timestampexpires_at silently ignored; session expires after 30 minutes
Caller sends expires_in (seconds) to REST APISession expires after the specified number of secondsWorks as intended
Caller uses the SDK with expires_atSDK sends expires_at to the REST APIexpires_at silently ignored at the API boundary

There is no error, warning, or validation failure returned to the caller when expires_at is supplied. This makes the drift invisible without careful inspection of session expiry times.


Affected Files

  • src/app/api/v1/checkout/sessions/route.ts — REST API schema uses expires_in instead of expires_at
  • src/lib/calmony-pay/client.ts — SDK exposes expires_at per the pinned spec

What to Do Now

Until this is resolved in a follow-up release, integrators should be aware of the following:

If you call the REST API directly

Use expires_in (seconds) rather than expires_at (Unix timestamp):

POST /api/v1/checkout/sessions
Content-Type: application/json

{
  "expires_in": 3600
}

Valid values for expires_in:

ValueMeaning
3005 minutes (minimum)
180030 minutes (default)
36001 hour
8640024 hours (maximum)

If you use the SDK

Be aware that expires_at passed through the SDK will currently be silently dropped at the REST API layer. The session will use the default 30-minute TTL. This will be corrected in a follow-up release — check the Changelog for updates.


Resolution Plan

The REST API schema and the SDK contract will be aligned. The spec-pinned expires_at (Unix timestamp) is the authoritative field. The expires_in field will either be removed or mapped to expires_at at the API boundary. A migration note will be added to the changelog once the fix is released.


References

  • Pinned Spec: Calmony Pay REST API Contract
  • Changelog — v1.0.106
  • Affected endpoints: POST /api/v1/checkout/sessions