← Back to all posts
7 min readCentrali Team

How to Auto-Expire Records with Centrali TTL

Learn how to configure automatic record expiration using TTL. Set structure-level defaults, per-record overrides, and sliding expiration for sessions, promo codes, and temporary data.

FeatureTutorial

Every application accumulates temporary data — session tokens, verification codes, promotional offers, draft content. Without automatic cleanup, these records pile up, consume storage, and slow down queries. Manual cleanup scripts are fragile and easy to forget.

Centrali's Record TTL (Time-To-Live) solves this by letting you set an expiration time on any record. Once expired, records are automatically excluded from query results and permanently deleted by a background sweep. No cron jobs. No manual intervention.

What is Record TTL?

Record TTL is a built-in data expiration feature that works at the record level:

  1. Set a TTL — specify ttlSeconds (duration) or expiresAt (timestamp) when creating or updating a record
  2. Record gets an expiresAt timestamp — calculated automatically from the TTL
  3. Expired records disappear from queries — read-time filtering excludes them instantly
  4. Background sweep deletes permanently — runs every 2 minutes, publishes record.expired events

You can also set a default TTL on a structure, so every new record in that structure automatically expires after the configured duration.

Use Case 1: Session Tokens

Session management is the most common TTL use case. Set a 24-hour default on your Sessions structure, and every session record expires automatically.

Set Up the Structure

typescript
import { CentraliSDK } from '@centrali-io/centrali-sdk'; const centrali = new CentraliSDK({ workspaceId: 'my-workspace', clientId: process.env.CENTRALI_CLIENT_ID, clientSecret: process.env.CENTRALI_CLIENT_SECRET, }); // Set 24-hour default TTL on Sessions await centrali.structures.update('sessions-structure-id', { defaultTtlSeconds: 86400, // 24 hours });

Create a Session

typescript
const session = await centrali.createRecord('Sessions', { userId: 'user-123', deviceInfo: 'Chrome on macOS', lastActivity: new Date().toISOString(), }); console.log(session.data.expiresAt); // → "2026-02-28T10:00:00Z" (24 hours from now)

Sliding Expiration

Reset the TTL on each user action to keep active sessions alive:

typescript
// On every authenticated request await centrali.updateRecord('Sessions', sessionId, { lastActivity: new Date().toISOString(), }, { ttlSeconds: 86400 }); // Reset to 24 hours

Inactive sessions expire and are cleaned up automatically. Active sessions keep extending.

Use Case 2: Promotional Codes

Time-limited promotions need precise expiration. Use expiresAt for business deadlines and ttlSeconds for short-lived flash coupons.

Create a Promo with a Deadline

typescript
// Summer sale ends September 1st const promo = await centrali.createRecord('Promotions', { code: 'SUMMER2026', discountPercent: 20, minOrderTotal: 50, }, { expiresAt: '2026-09-01T00:00:00Z' });

Create a Flash Coupon

typescript
// One-hour flash deal const flash = await centrali.createRecord('Promotions', { code: 'FLASH50', discountPercent: 50, }, { ttlSeconds: 3600 });

Validate at Checkout

No special filtering needed. Expired promos are automatically excluded from queries:

typescript
const result = await centrali.queryRecords('Promotions', { 'data.code': customerCode, }); if (result.data.length === 0) { throw new Error('Invalid or expired promo code'); } // If we get here, the promo is valid and not expired const discount = result.data[0].data.discountPercent;

Use Case 3: Draft Content

Auto-expire abandoned drafts after 30 days. When a draft is published, remove the TTL so it lives permanently.

Configure the Structure

typescript
// Set 30-day default TTL on BlogPost structure await centrali.structures.update('blogpost-structure-id', { defaultTtlSeconds: 2592000, // 30 days });

Create a Draft

New drafts inherit the 30-day TTL automatically:

typescript
const draft = await centrali.createRecord('BlogPost', { title: 'Work in Progress', content: 'This is a rough draft...', status: 'draft', }); console.log(draft.data.expiresAt); // 30 days from now

Publish and Clear TTL

typescript
await centrali.updateRecord('BlogPost', draft.id, { status: 'published', publishedAt: new Date().toISOString(), }, { clearTtl: true }); // Make permanent

The published post now has expiresAt: null and will never be automatically deleted.

Using the REST API

All TTL operations are available via query parameters on the REST API.

Create with TTL

bash
curl -X POST "https://api.centrali.io/data/workspace/acme/api/v1/records?ttlSeconds=3600" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "structureId": "str_sessions", "data": { "userId": "user-123", "token": "abc-xyz" } }'

Remove TTL

bash
curl -X PATCH "https://api.centrali.io/data/workspace/acme/api/v1/records/rec_xyz789?clearTtl=true" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "data": {} }'

Set Structure Default

bash
curl -X PUT "https://api.centrali.io/data/workspace/acme/api/v1/structures/str_sessions" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "defaultTtlSeconds": 86400 }'

What Happens Under the Hood

When you set a TTL on a record, three things happen:

  1. Immediate: The record gets an expiresAt timestamp in the database
  2. On every query: A read-time filter (WHERE expiresAt IS NULL OR expiresAt > NOW()) excludes expired records from all API responses — lists, gets, counts, and search results
  3. Every 2 minutes: A background sweep acquires a distributed Redis lock, finds expired records in batches, runs each through the full delete pipeline (storage cleanup, search index removal), and publishes a record.expired NATS event for each

The record.expired event lets you trigger downstream cleanup — revoke OAuth tokens, send expiration notifications, or update analytics.

Get Started

Install the SDK and start using TTL in three lines:

bash
npm install @centrali-io/centrali-sdk
typescript
import { CentraliSDK } from '@centrali-io/centrali-sdk'; const centrali = new CentraliSDK({ workspaceId: 'my-workspace', clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET' }); // Create a record that expires in 1 hour await centrali.createRecord('Sessions', { userId: 'user-123' }, { ttlSeconds: 3600 });

Check the Record TTL documentation for the full reference, including TTL priority rules, best practices, and more use cases.

Building something with Centrali and want to share feedback about this feature?

Email feedback@centrali.io