env.<BINDING> (a KvNamespace) is the in-function handle to a KV namespace. It’s a thin, pre-authenticated wrapper over the KV REST API — the runtime injects the credential, so your code holds no API key.
interface KvNamespace {
get(key: string, options?: KvGetTextOptions): Promise<string | null>;
get<T>(key: string, options: KvGetJsonOptions): Promise<T | null>;
put(key: string, value: string, options?: KvPutOptions): Promise<void>;
delete(key: string): Promise<void>;
list(options?: KvListOptions): Promise<KvListResult>;
}
Key behaviors:
- Values are opaque bytes —
put stores the string you pass verbatim (no base64, no envelope); get returns it byte-for-byte.
- Missing keys read as
null — get resolves to null for a key that doesn’t exist, not an error.
delete is idempotent — deleting a missing key succeeds.
- Read-your-writes — a read after a successful
put from the same location reflects it. See Consistency.
- Errors throw — a non-2xx from the store (other than the
404→null on get) rejects the promise with an Error describing the operation and status.
get(key, options?)
Read a value. Two overloads, selected by options.type:
interface KvGetTextOptions { type?: "text" } // default
interface KvGetJsonOptions { type: "json" } // JSON.parse the stored value
// Text (default) -> string | null
const raw = await env.MY_KV.get("user/123");
// JSON -> T | null (JSON.parse applied; an empty stored value yields null)
const user = await env.MY_KV.get<{ name: string }>("user/123", { type: "json" });
Returns null if the key does not exist. With { type: "json" }, a malformed stored value throws from JSON.parse.
put(key, value, options?)
Write a value. value is a string, stored verbatim. Resolves once the write is acknowledged.
interface KvPutOptions {
expirationTtl?: number; // seconds until the key expires server-side
/** @deprecated Not supported by the API. Accepted but ignored. */
metadata?: unknown;
}
await env.MY_KV.put("user/123", JSON.stringify({ name: "Alice" }));
// Expire automatically after one hour
await env.MY_KV.put("session/abc", token, { expirationTtl: 3600 });
expirationTtl maps to the REST API’s ?ttl_secs= parameter: the key is deleted server-side roughly that many seconds after the write. The value is floored to a whole number of seconds; anything below 1 is not sent — the write succeeds without a TTL. See Key Expiration.
expirationTtl requires @telnyx/edge-runtime ≥ 0.2.2 — earlier versions accept it but silently ignore it. metadata is ignored on every version (KV has no per-key metadata); it remains on the type, deprecated, so code that sets it keeps compiling.
delete(key)
Remove a key. Idempotent — deleting a missing key resolves normally.
await env.MY_KV.delete("user/123");
list(options?)
Enumerate keys (names only — list does not return values).
interface KvListOptions {
prefix?: string;
limit?: number;
cursor?: string; // from a previous result's `cursor`
}
interface KvListResult {
keys: KvKeyInfo[];
list_complete: boolean;
cursor?: string; // present when list_complete is false
}
interface KvKeyInfo {
name: string;
sizeBytes?: number; // stored value size, from the API's size_bytes
updatedAt?: Date; // last write time, from the API's updated_at
/** @deprecated Not populated by the API. Always undefined. */
metadata?: unknown;
}
const { keys } = await env.MY_KV.list({ prefix: "user/" });
// [{ name: "user/123", sizeBytes: 21, updatedAt: 2026-06-18T14:48:17.475Z }, …]
list() requires @telnyx/edge-runtime ≥ 0.2.1 — on 0.2.0 it throws Unexpected KV list response shape, because that release’s parser predates the current API list format. sizeBytes and updatedAt are populated from 0.2.2; on 0.2.1 entries carry only name.
KvKeyInfo.metadata is never populated — KV has no per-key metadata. It remains on the type, deprecated, so code that reads it keeps compiling.
- Overview — the KV Runtime API surface at a glance
- Quick Start — declare the binding and type it
- REST API — the same operations over HTTP