> ## 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 at turn granularity.

`this.ctx.storage` (an `ActorStorage`) is the actor's per-key persistent storage. **A turn that returns successfully is persisted** — all writes made during the turn (put/delete/transaction/sql.exec/setAlarm) commit atomically at end-of-turn; a turn that throws commits nothing.

```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>;

  readonly sql: SqlStorage;                          // per-actor embedded SQLite (synchronous)
  transactionSync<T>(fn: () => T): T;                // atomic SQL transaction (synchronous)

  // Alarm API (mirrors ctx.setAlarm)
  // Available in local development; prod support is pending.
  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".

## `sql`

`ctx.storage.sql` is a private embedded SQLite database for this actor — a separate,
synchronous store beside the key/value surface above. See
[SQL storage](/docs/edge-compute/stateful-actors/guides/storage/sql) for the `exec` /
cursor contract, types, transactions, limits, and durability.

## `transactionSync(fn)`

Atomic SQL transaction: runs `fn` synchronously, commits its `sql.exec` writes when it
returns (passing the return value through), rolls all of them back if it throws. `fn` must
not be `async` — an async callback throws `SqlAsyncTransactionError` and commits nothing.
This is the only transaction API for SQL: `exec()` rejects raw `BEGIN` / `COMMIT` /
`ROLLBACK` / `SAVEPOINT` with `SqlTransactionControlError`. Unlike a thrown
[`transaction(fn)`](#transactionfn), a thrown `transactionSync` does **not** poison the
turn: catch it and the method call completes normally.

## Related

* [Key-Value storage](/docs/edge-compute/stateful-actors/guides/storage/key-value) — the `get`/`put`/`list` guide
* [SQL storage](/docs/edge-compute/stateful-actors/guides/storage/sql) — the `ctx.storage.sql` guide
* [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
