Skip to main content

Telnyx Storage: KV — Full Documentation

Complete page content for KV (Storage section) of the Telnyx developer docs (https://developers.telnyx.com). This file: https://developers.telnyx.com/development/llms/storage-kv-llms-full-txt.md · Root index: https://developers.telnyx.com/llms.txt

Get Started

KV

Source: https://developers.telnyx.com/docs/edge-compute/kv.md
KV is a globally distributed key-value store: you write bytes under a string key and read them back, fast, from anywhere. It is built for read-heavy edge workloads — session data, cached responses, feature flags, and other small values a function needs on every request. A value is opaque bytes. You choose the serialization (text, JSON, binary); KV stores exactly what you send and returns it byte-for-byte. There is no envelope, no base64 encoding, and no server-side interpretation of the value.

Two Ways to Use KV

The same namespaces and keys are reachable two ways. Pick based on where your code runs. Both hit the same store, so a value written through the binding is immediately readable over REST and vice versa. The binding is a thin, pre-authenticated wrapper over the same REST endpoints — it just means your function never handles an API key. The complete endpoint reference — namespaces and keys, with request/response schemas and code samples — is generated from the OpenAPI spec and lives in the REST API group of this KV section in the sidebar. The env KV binding is TypeScript-only and requires @telnyx/edge-runtime ≥ 0.2.2. Go, JS, Python, and Quarkus functions use the REST API directly.

Next Steps

  • Quick Start — Create a namespace, bind it, read and write
  • How KV Works — Keys, TTL, and the consistency model
  • Examples — Session storage, caching, and feature flags
  • Runtime API — the env binding surface (KvNamespace)
  • CLI Commands — Manage KV from the command line
  • Pricing — Free tier and paid plans
  • Bindings — How the env binding surface works
  • Secrets — Secure credential storage

Quick Start

Source: https://developers.telnyx.com/docs/edge-compute/kv/quick-start.md
Get up and running with KV: create a namespace, then use it from a TypeScript function through an env binding, or from anywhere through the REST API.

1. Create a Namespace

A namespace is an isolated key space. Create one with the CLI or the API.
The response includes the namespace id (a UUID) — you’ll need it in the next step. A new namespace starts in status: "pending" and isn’t writable yet: writes return 409 ("Namespace is not ready (status: pending)") until provisioning finishes, which typically takes a few seconds and can stretch to ~20. If you’re scripting, poll GET https://api.telnyx.com/v2/storage/kvs/{id} until "status": "provision_ok" before your first write. (With the binding path below you rarely notice — editing func.toml and deploying already takes longer than provisioning.)

Path A: The Function Binding

Recommended for TypeScript edge functions. The runtime injects the credential, so your code holds no API key.

2. Bind the Namespace

Declare the namespace in func.toml. The block key is a name you choose — it’s not a reserved word — and it becomes the property on env. This example uses MY_KV, so the binding is reached as env.MY_KV:
Add @telnyx/edge-runtime (≥ 0.2.2) to your package.json dependencies, then regenerate the environment types:
Each [storage.kv.<NAME>] block becomes env.<NAME>: KvNamespace in the generated telnyx-env.d.ts — declare as many namespaces as you need. KV type generation requires CLI ≥ v0.2.3 (earlier releases report the block as an unrecognized key and write an empty Env). The binding itself resolves at runtime from func.toml — types are for the compiler, and a stale telnyx-env.d.ts doesn’t affect the deployed function.

3. Use env.MY_KV in Your Code

The binding surface: list() returns key metadata, not values — { keys: [{ name, sizeBytes, updatedAt }], list_complete, cursor? }. Paginate by passing the returned cursor back in list({ cursor }). On 0.2.1 entries carry only name; 0.2.0 throws a response-shape error. put’s expirationTtl option requires ≥ 0.2.2 — earlier versions accept it but silently ignore it. The metadata option is deprecated and ignored on every version. See Key Expiration.

Path B: The REST API

Use this anywhere outside a TypeScript edge function — a non-TypeScript function (Go, JS, Python, Quarkus), your own backend, or tooling. Authenticate with your TELNYX_API_KEY (the SDKs read it from the environment). Whether you use an SDK or plain HTTP, the value is the raw request/response body — no base64, no envelope. KV support landed in the official server SDKs in telnyx-node ≥ 7.5.0, telnyx-python ≥ 4.166.0, telnyx-php ≥ 7.88.0 (see the PHP tab for the required version pin), telnyx-ruby ≥ 5.152.0, and telnyx-go ≥ v4.85.0 — on earlier versions the storage resource is object storage (buckets) only. The Java SDK doesn’t cover KV yet; call the endpoints over plain HTTP as in the curl tab.
Install with composer require "telnyx/telnyx-php:^7.88" guzzlehttp/guzzle. The pin matters: the semver-highest tag v8.0.0 predates KV, so an unpinned composer require telnyx/telnyx-php installs a version with no storage->kvs at all. Guzzle (or any PSR-18 client) is required because the SDK doesn’t bundle one — without it new Client() throws a discovery exception.
The gem requires Ruby ≥ 3.2. On Ruby ≥ 3.4, also gem install base64 — telnyx 5.152.0 loads it but doesn’t yet declare it as a dependency.
Server-side TTL (ttl_secs), its error cases, and an inspectable application-level alternative are covered in Key Expiration. list returns key names and per-key metadata, never values:
When meta.has_more is true, pass the returned meta.cursor back as ?cursor= (in the SDKs, the cursor parameter) to fetch the next page — key listing does not auto-paginate in any SDK. Inside an edge function, the org binding injects TELNYX_API_KEY (and a base-URL proxy) at runtime, so REST calls from a function authenticate without you shipping a key. Next: Best Practices for key naming, serialization, and error handling.

Concepts

How KV Works

Source: https://developers.telnyx.com/docs/edge-compute/kv/concepts/how-kv-works.md
KV is a single global key-value store optimized for low-latency reads from edge functions. A value is opaque bytes — you choose the serialization (text, JSON, binary), and KV stores exactly what you send and returns it byte-for-byte, with no envelope, base64 encoding, or server-side interpretation.

Keys

A key is a path-like string. Allowed characters are a-z, A-Z, 0-9, and - _ / = .. Use / to group related keys (for example user/123, session/abc). Colons (:) are not allowed.

Expiration (TTL)

By default a value lives until you delete it. You can also set a server-side TTL so a key expires automatically: pass expirationTtl on a binding put (env.MY_KV.put(key, value, { expirationTtl: 30 }), requires @telnyx/edge-runtime ≥ 0.2.2), ttl_secs on a REST write (PUT …/keys/{key}?ttl_secs=N), or --ttl on the CLI (telnyx-edge storage kv key put … --ttl 30s). The TTL is a whole number of seconds; once it elapses the key is gone and reads return null/404. See Key Expiration.

No Per-Key Metadata

KV has no per-key metadata. The binding’s put accepts a metadata option so older code keeps compiling, but it is ignored (deprecated as of 0.2.2), and list never returns metadata.

Consistency and Regionality

KV is a single global store. There is no region to choose at creation time and no per-region copies to reconcile — every namespace is one logical dataset reachable from every edge location. Writes are replicated for durability and committed by quorum before they’re acknowledged.
  • Read-your-writes from a given location is reliable: once a write returns, a subsequent read sees it.
  • Across locations, a read issued immediately after a write elsewhere may briefly observe the previous value; treat cross-location visibility as near-real-time rather than instantaneous.
  • Distance costs latency, not staleness — a location far from where the data is coordinated pays network round-trip on the request, but reads the same authoritative data as everywhere else.
  • No transactions or compare-and-swap. Don’t use KV for atomic read-modify-write, counters, or coordination — concurrent writers to one key are last-write-wins.

Key Expiration

Source: https://developers.telnyx.com/docs/edge-compute/kv/ttl-and-metadata.md
KV supports server-side expiration (TTL): set a TTL on a write and the key is deleted automatically once it elapses. Without a TTL, a value lives until you delete it. KV has no per-key metadata.

Server-Side TTL

Pass expirationTtl on a binding put, a ttl_secs query parameter on a REST write, or --ttl on the CLI. The value is a whole number of seconds (19223372036); the key expires roughly that many seconds after the write, after which reads return null/404.
An invalid ttl_secs (non-integer, 0, or negative) is rejected with 422 and the key is not written. The binding never produces that 422: it floors expirationTtl to a whole number of seconds and, if the result is less than 1, sends no TTL at all — the write succeeds and the key does not expire. There is no way to read the remaining TTL back — a get/list on a live key does not report its expiry. expirationTtl requires @telnyx/edge-runtime ≥ 0.2.2. Earlier versions accept the option but silently ignore it — the key is written without a TTL.

No Per-Key Metadata

KV stores values as opaque bytes and has no per-key metadata. The binding’s put still accepts a metadata option so older code keeps compiling, but it is ignored (and marked @deprecated as of @telnyx/edge-runtime 0.2.2), and list never returns metadata. Anything you put in the value itself (including JSON that looks like those fields) is stored verbatim, not interpreted.

Application-Level Expiry

Server-side TTL deletes the key and tells you nothing else — there is no way to read a key’s remaining lifetime. Use this pattern instead when you want an absolute expires_at timestamp you can inspect on read (or when you’re pinned to @telnyx/edge-runtime < 0.2.2, where expirationTtl is ignored). Wrap your value with the timestamp and check it when you read; if it’s in the past, treat the key as missing (and optionally delete it).
Using the REST API instead? You’d normally reach for ttl_secs above. Build this envelope on top of the REST API examples from the Quick Start only when you need the inspectable expires_at. 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. Prefer native TTL (expirationTtl/ttl_secs) for eager server-side cleanup; if you do need a sweep, drive it from an external scheduler hitting your function over HTTP — HTTP is the only function trigger today.
  • 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.

Examples

Session Storage

Source: https://developers.telnyx.com/docs/edge-compute/kv/examples/session-storage.md
Store user sessions at the edge and expire them after a day. This uses the KV binding (bound as MY_KV) with a server-side TTL: pass expirationTtl on the write and KV deletes the session automatically once it elapses. Each write renews the TTL, so an active session slides forward and an abandoned one expires. Requires @telnyx/edge-runtime ≥ 0.2.2.
Non-TypeScript functions get the same behavior with the ttl_secs parameter on a REST API write. If you need to inspect when a session expires, use the application-level envelope instead — see Key Expiration.

API Response Caching

Source: https://developers.telnyx.com/docs/edge-compute/kv/examples/api-response-caching.md
Cache expensive upstream responses for a few minutes. This uses the KV binding (bound as MY_KV) with a server-side TTL: pass expirationTtl on the write and KV deletes the key automatically once it elapses — no cleanup code. Requires @telnyx/edge-runtime ≥ 0.2.2.
Non-TypeScript functions get the same behavior with the ttl_secs parameter on a REST API write. If you need to inspect when an entry expires, use the application-level envelope instead — see Key Expiration.

Feature Flags

Source: https://developers.telnyx.com/docs/edge-compute/kv/examples/feature-flags.md
Read flags on the request path — no expiry needed, so use the KV binding (env.MY_KV) directly.
Flip a flag without redeploying — from the CLI:

Reference

Overview

Source: https://developers.telnyx.com/docs/edge-compute/kv/reference.md
The types in this reference are exported from @telnyx/edge-runtime (TypeScript) and describe version ≥ 0.2.2 — the first release where expirationTtl is applied and list() entries carry sizeBytes/updatedAt. They describe the env binding — the in-function surface. To read or write KV from another language or outside a function, use the REST API. The KV Runtime API is a single binding type and the small set of option/result types its methods take. A namespace declared as [storage.kv.&lt;NAME>] in func.toml resolves on env.&lt;NAME> as a KvNamespace.

Getting the Binding

Declaring the binding is covered in the Quick Start. The binding resolves at runtime from func.toml; run telnyx-edge types (CLI ≥ v0.2.3) after editing the manifest to regenerate telnyx-env.d.ts, which types env.&lt;NAME> as a KvNamespace.
  • KvNamespace — the method-by-method reference
  • Bindings — how bindings resolve on env
  • REST API — the same operations over HTTP
  • Key Expiration — server-side TTL via expirationTtl, ttl_secs, or --ttl

KvNamespace

Source: https://developers.telnyx.com/docs/edge-compute/kv/reference/kv-namespace.md
env.&lt;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.
Key behaviors:
  • Values are opaque bytesput stores the string you pass verbatim (no base64, no envelope); get returns it byte-for-byte.
  • Missing keys read as nullget 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 404null 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:
Returns null if the key does not exist. With &#123; type: "json" &#125;, 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.
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.

list(options?)

Enumerate keys (names only — list does not return values).
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

CLI

Source: https://developers.telnyx.com/docs/edge-compute/kv/cli.md
Manage KV namespaces and keys using the telnyx-edge CLI.

Namespace Management

Key Operations

The value is stored verbatim — pass it as a positional argument, or use --path to store the contents of a file.

Key Put Flags

Key List Flags

Keys may contain a-z, A-Z, 0-9, and - _ / = . (no colons). Use --ttl for server-side expiry (see Key Expiration). KV has no per-key metadata, so there is no --metadata flag.

Best Practices

Source: https://developers.telnyx.com/docs/edge-compute/kv/best-practices.md
Practical guidance for working with KV, whether through the env binding or the REST API.

Key Naming

Keys may contain a-z, A-Z, 0-9, and - _ / = . (no colons). Use / to group related keys:
Grouping by prefix also lets you enumerate a subset later — list(&#123; prefix: "user/" &#125;) (or ?prefix=user/ over REST).

Value Serialization

KV stores values verbatim, so serialize complex values yourself (no base64 needed):

Missing Keys

get returns null for a key that doesn’t exist — handle it explicitly:

Keep Values Small

KV is built for many small values read on the request path, not for large blobs. A value is capped at 1 MiB (1,048,576 bytes) — a larger write is rejected with 413. Store big or binary objects in Cloud Storage and keep only the key or a small reference in KV.

Limits

Don’t Rely on Atomicity

KV has no transactions or compare-and-swap, and concurrent writers to one key are last-write-wins. Don’t use it for counters, locks, or coordination — see Consistency.

Platform

Pricing

Source: https://developers.telnyx.com/docs/edge-compute/kv/pricing.md
KV pricing is based on operations and storage. Egress is free.

Pricing

Egress is free. No charges for data transferred out of KV.

API Reference (KV)

kv namespaces

  • List KV namespaces: Lists the KV namespaces for the authenticated user’s organization. Results use page-based pagination (page[number]/page[size]).
  • Create a KV namespace: Creates a new KV namespace. Provisioning is asynchronous: the namespace is returned with status pending and becomes usable once it reaches provision_ok.
  • Get a KV namespace: Retrieves a KV namespace by its ID, including its provisioning status.
  • Delete a KV namespace: Deletes a KV namespace and all of the keys it contains. Deletion is asynchronous: the namespace is returned with status deleting. Deleting a namespace whose…

kv keys

  • List keys: Lists the keys in a namespace. Returns key names and metadata only, never values. Results are paginated with limit and an opaque cursor.
  • Get a key’s value: Returns the raw stored value for a key. The response body is the value exactly as it was written; the Content-Type header echoes the value’s stored content t…
  • Set a key’s value: Creates or replaces the value for a key. The request body is stored verbatim as the value — no base64, no JSON envelope — up to 1 MiB. The request’s `Content-T…
  • Delete a key: Deletes a key. Idempotent: deleting a key that does not exist still succeeds. The namespace itself must exist and be provisioned.

cloudfs filesystems

  • List CloudFS filesystems: Lists the CloudFS filesystems for the authenticated user’s organization. Results use cursor-based pagination: fetch the next page by passing `meta.cursors.afte…
  • Create a CloudFS filesystem: Creates a CloudFS filesystem. Provisioning is synchronous — typically a few seconds, up to a few minutes — and the filesystem is returned with status ready,…
  • Get a CloudFS filesystem: Retrieves a CloudFS filesystem by its ID. The returned meta_url omits the credential — the metadata token is only ever returned by create and rotate-meta-tok…
  • Update a CloudFS filesystem: Updates a CloudFS filesystem. Only name can be changed; other fields are immutable and unknown fields are rejected with a 400. Renaming to a name that alre…
  • Delete a CloudFS filesystem: Permanently deletes a CloudFS filesystem, removing its S3 bucket and its metadata database. Deletion is synchronous: the response returns the filesystem’s fina…
  • Rotate the metadata token: Issues a new metadata access token for the filesystem and returns the full filesystem, including the new meta_token and credential-bearing meta_url. The pr…