> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Key expiration

> Telnyx KV has no server-side TTL or metadata. Expire keys at the application level by storing an expiry timestamp inside the value and checking it on read.

KV stores values as opaque bytes. It has **no server-side TTL and no per-key metadata** — a value is exactly the bytes you write, and it lives until you delete it.

<Note>
  There are no `expiration_ttl`, `expiration`, or `metadata` fields on a write, and no `--ttl`/`--metadata` CLI flags. Anything you put in the request body (including JSON that looks like those fields) is stored verbatim as the value, not interpreted.
</Note>

## Expiring keys at the application level

To get expiry behavior, wrap your value with an `expires_at` timestamp and check it when you read. If it's in the past, treat the key as missing (and optionally delete it).

```javascript theme={null}
// Built on the raw kvGet/kvPut from the Quick Start.

async function kvPutWithExpiry(key, value, ttlSeconds) {
    const envelope = JSON.stringify({
        value,
        expires_at: Date.now() + ttlSeconds * 1000,
    });
    await kvPut(key, envelope);
}

async function kvGetWithExpiry(key) {
    const raw = await kvGet(key);
    if (raw === null) return null;                 // key not found

    const { value, expires_at } = JSON.parse(raw);
    if (Date.now() > expires_at) {
        await kvDelete(key);                        // lazily clean up
        return null;                                // expired
    }
    return value;
}

// kvDelete helper
async function kvDelete(key) {
    await fetch(`${BASE}/${encodeURIComponent(key)}`, {
        method: "DELETE",
        headers: { "Authorization": `Bearer ${API_KEY}` },
    });
}
```

```javascript theme={null}
// Usage: a session that "expires" after one hour
await kvPutWithExpiry("session/abc", JSON.stringify({ userId: 42 }), 3600);

const session = await kvGetWithExpiry("session/abc"); // null once an hour has passed
```

Notes on this pattern:

* **Reads do the enforcing.** An expired key still occupies storage until it's read (and lazily deleted) or you delete it explicitly. Run a periodic sweep with [cron triggers](/docs/edge-compute/configuration/cron-triggers) if you need eager cleanup.
* **Use a consistent clock.** `Date.now()` on the edge node is fine for coarse expiry; don't rely on it for sub-second precision.
* **Keep the envelope small.** You pay for stored bytes, so the wrapper adds a little overhead per key.
