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:
- Set a TTL — specify
ttlSeconds(duration) orexpiresAt(timestamp) when creating or updating a record - Record gets an
expiresAttimestamp — calculated automatically from the TTL - Expired records disappear from queries — read-time filtering excludes them instantly
- Background sweep deletes permanently — runs every 2 minutes, publishes
record.expiredevents
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
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
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:
// On every authenticated request
await centrali.updateRecord('Sessions', sessionId, {
lastActivity: new Date().toISOString(),
}, { ttlSeconds: 86400 }); // Reset to 24 hoursInactive 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
// 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
// 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:
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
// 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:
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 nowPublish and Clear TTL
await centrali.updateRecord('BlogPost', draft.id, {
status: 'published',
publishedAt: new Date().toISOString(),
}, { clearTtl: true }); // Make permanentThe 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
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
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
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:
- Immediate: The record gets an
expiresAttimestamp in the database - 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 - 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.expiredNATS 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:
npm install @centrali-io/centrali-sdkimport { 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.