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

# Actor Storage

> Per-key persistent storage for a Stateful Actor, accessed as this.ctx.storage — get/put/delete/list/transaction plus the alarm API. Writes that return successfully are persisted.

`this.ctx.storage` (an `ActorStorage`) is the actor's per-key persistent storage. **Writes that return successfully are persisted.**

```ts theme={null}
interface ActorStorage {
  get<T = unknown>(key: string): Promise<T | undefined>;
  put<T = unknown>(key: string, value: T): Promise<void>;
  delete(key: string): Promise<boolean>;            // true iff the key existed
  list<T = unknown>(options?: ListOptions): Promise<Map<string, T>>;
  deleteAll(): Promise<void>;                        // atomic clear of all keys; does NOT clear a pending alarm
  transaction<T>(fn: (txn: StorageTransaction) => Promise<T>): Promise<T>;

  // Alarm API (mirrors ctx.setAlarm)
  setAlarm(when: number): Promise<void>;
  getAlarm(): Promise<number | null>;
  deleteAlarm(): Promise<void>;
}
```

Key behaviors:

* **Lazy per `get`** — state is not preloaded on activation. Call `get` when you need it.
* **Read-your-writes** — reads always reflect prior writes from the same actor.
* **Per-actor method serialization** — calls to one actor instance are dispatched one at a time, giving you effective ACID at the actor level.
* **Values round-trip through a codec.** JSON natives plus `Date`, `Map`, `Set`, typed arrays, `ArrayBuffer`, `BigInt`, and `RegExp` come back as what you stored. Unstorable values — functions, class instances, circular structures, promises, streams — are rejected with a `CodecError` at the write.
* **Keys starting with `__telnyx_` are reserved** for the runtime's own bookkeeping — writes to them are rejected.

## `list(options)`

Lexicographic key order; `reverse: true` flips it. Returns a `Map<string, T>` to preserve ordering.

```ts theme={null}
interface ListOptions {
  prefix?: string;        // filter to keys starting with this string
  start?: string;         // inclusive
  startAfter?: string;    // exclusive
  end?: string;           // exclusive
  reverse?: boolean;
  limit?: number;         // default 128, max 1000 — a larger limit is rejected
}
```

## `transaction(fn)`

Atomic batch. `fn` receives a `StorageTransaction` with the same `get`/`put`/`delete`/`list` surface.

```ts theme={null}
await this.ctx.storage.transaction(async (txn) => {
  const v = (await txn.get<number>("count")) ?? 0;
  await txn.put("count", v + 1);
  await txn.put("updated_at", Date.now());
});
```

If `fn` throws, the buffered writes are discarded — **and the enclosing method call
fails with `ActorOutputGateError`**, even if your code catches the rejection. Only the
transaction's own writes are undone: writes you make after catching the rejection still
commit, but the caller sees the error instead of your return value. A thrown transaction
is not a control-flow tool for "try the write, continue on failure".

## Related

* [Actor Context](/docs/edge-compute/stateful-actors/api-reference/context) — `ctx.storage` lives here
* [Alarms](/docs/edge-compute/stateful-actors/alarms) — the `setAlarm` / `getAlarm` / `deleteAlarm` contract
