Skip to main content
this.ctx.storage (an ActorStorage) is the actor’s per-key persistent storage. Writes that return successfully are persisted.
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.
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.
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”.
  • Actor Contextctx.storage lives here
  • Alarms — the setAlarm / getAlarm / deleteAlarm contract