Skip to content

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:

  1. Explicit expiresAt — if provided, used as-is
  2. ttlSeconds — if provided, expiresAt = now + ttlSeconds
  3. Structure defaultTtlSeconds — if the structure has a default, expiresAt = now + defaultTtlSeconds
  4. 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

  1. Navigate to Structures → select your structure → Settings tab
  2. Under Default Record TTL, enter the duration in seconds
  3. 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

await centrali.structures.update('structure-uuid', {
  defaultTtlSeconds: 86400, // 24 hours
});

To remove the default TTL:

await centrali.structures.update('structure-uuid', {
  defaultTtlSeconds: null,
});

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:

await centrali.updateRecord('Sessions', 'record-id', {}, { ttlSeconds: 7200 });

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:

await centrali.updateRecord('Sessions', 'record-id', {}, { clearTtl: true });

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:

  1. Acquires a distributed lock (Redis) to prevent concurrent sweeps
  2. Queries records where expiresAt <= NOW() in batches
  3. Runs each through the full delete pipeline (storage cleanup, search index removal)
  4. Publishes a record.expired NATS 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

  1. Use structure defaults for consistent expiration — Set defaultTtlSeconds on structures like sessions or temporary tokens rather than setting TTL on every create call.

  2. Use expiresAt for business deadlines — For promo codes ending on a specific date, use expiresAt instead of calculating seconds.

  3. 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.

  4. Subscribe to record.expired — If you need to run cleanup logic (e.g., revoke tokens, notify users), set up a compute function trigger on the record.expired event.

  5. 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)