Record TTL (Time-To-Live)¶
Record TTL lets you set an expiration time on individual records or apply a default expiration to all records in a structure. Expired records are automatically excluded from query results and permanently deleted by a background sweep.
Key Concepts¶
| Concept | Description |
|---|---|
| TTL | Duration in seconds until a record expires |
| expiresAt | Explicit expiration timestamp (ISO 8601) |
| defaultTtlSeconds | Structure-level default applied to new records |
| Background sweep | Runs every 2 minutes, permanently deletes expired records |
| record.expired | NATS event emitted when a record is swept |
TTL Priority¶
When creating a record, TTL is resolved in this order:
- Explicit
expiresAt— if provided, used as-is ttlSeconds— if provided,expiresAt = now + ttlSeconds- Structure
defaultTtlSeconds— if the structure has a default,expiresAt = now + defaultTtlSeconds - No TTL — record never expires
Setting a Default TTL on a Structure¶
Configure a default TTL so all new records in a structure automatically expire after a set duration.
Via Console UI¶
- Navigate to Structures → select your structure → Settings tab
- Under Default Record TTL, enter the duration in seconds
- Click Save
New records will inherit this TTL unless overridden at creation time.
Via API¶
curl -X PUT "https://api.centrali.io/workspace/acme/api/v1/structures/str_sessions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"defaultTtlSeconds": 86400
}'
Via SDK¶
To remove the default TTL:
Creating Records with TTL¶
Using ttlSeconds¶
Set a TTL in seconds. The record expires after that duration from creation.
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"
}
}'
SDK:
const session = await centrali.createRecord('Sessions', {
userId: 'user-123',
token: 'abc-xyz',
}, { ttlSeconds: 3600 });
console.log(session.data.expiresAt); // ISO timestamp ~1 hour from now
Using expiresAt¶
Set an explicit expiration timestamp:
curl -X POST "https://api.centrali.io/data/workspace/acme/api/v1/records?expiresAt=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"structureId": "str_promotions",
"data": {
"code": "SUMMER2026",
"discount": 20
}
}'
SDK:
const promo = await centrali.createRecord('Promotions', {
code: 'SUMMER2026',
discount: 20,
}, { expiresAt: '2026-09-01T00:00:00Z' });
Updating TTL on Existing Records¶
Extend or Change TTL¶
# Reset TTL to 2 hours from now
curl -X PATCH "https://api.centrali.io/data/workspace/acme/api/v1/records/rec_xyz789?ttlSeconds=7200" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": {} }'
SDK:
Remove TTL (Make Permanent)¶
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": {} }'
SDK:
How Expiration Works¶
Read-Time Filtering¶
All record queries automatically exclude expired records. You do not need to add any filter — expired records simply stop appearing in results from GET, list, query, and count endpoints.
Background Sweep¶
A background worker runs every 2 minutes to permanently delete expired records:
- Acquires a distributed lock (Redis) to prevent concurrent sweeps
- Queries records where
expiresAt <= NOW()in batches - Runs each through the full delete pipeline (storage cleanup, search index removal)
- Publishes a
record.expiredNATS event for each deleted record
record.expired Event¶
{
"event": "record.expired",
"timestamp": "2026-02-27T10:00:00Z",
"workspace": "acme",
"data": {
"record": {
"id": "rec_xyz789",
"structureId": "str_sessions"
}
}
}
You can subscribe to this event in compute function triggers to run cleanup logic when records expire.
Response Format¶
Records with a TTL include an expiresAt field in the response:
{
"id": "rec_xyz789",
"structureId": "str_sessions",
"data": {
"userId": "user-123",
"token": "abc-xyz"
},
"expiresAt": "2026-02-27T11:00:00Z",
"createdAt": "2026-02-27T10:00:00Z",
"updatedAt": "2026-02-27T10:00:00Z"
}
Records without a TTL have expiresAt: null.
Best Practices¶
-
Use structure defaults for consistent expiration — Set
defaultTtlSecondson structures like sessions or temporary tokens rather than setting TTL on every create call. -
Use
expiresAtfor business deadlines — For promo codes ending on a specific date, useexpiresAtinstead of calculating seconds. -
TTL is not a compliance tool — Do not rely on TTL alone for regulatory data retention. Use it for operational cleanup alongside proper data governance.
-
Subscribe to
record.expired— If you need to run cleanup logic (e.g., revoke tokens, notify users), set up a compute function trigger on therecord.expiredevent. -
Extend TTL on activity — For session-like records, update the TTL on each user action to implement sliding expiration.
Common Use Cases¶
| Use Case | Structure | TTL Strategy |
|---|---|---|
| Session tokens | sessions | defaultTtlSeconds: 86400 (24h) |
| Email verification codes | verification-codes | ttlSeconds: 900 (15 min) |
| Promotional codes | promotions | expiresAt: '2026-12-31T23:59:59Z' |
| Draft content | drafts | defaultTtlSeconds: 2592000 (30 days) |
| Rate limit counters | rate-limits | ttlSeconds: 60 (1 min) |
| Temporary file links | temp-links | ttlSeconds: 3600 (1h) |
Related Documentation¶
- Structures & Records — Core data management
- Records API — Full API reference
- Structures API — Structure configuration
- SDK Reference — JavaScript/TypeScript SDK
- Event Payloads — NATS event reference