---
name: memwry
description: Use when an AI agent needs short-lived (6-12h) working memory that survives restarts, hands off state to another agent, or caches an expensive result — via x402 USDC micropayments on Base. Trigger on "remember this for later", "cache this result", "hand off state to another agent", "survive a restart/crash", or any mention of Memwry.
---
# Memwry — Ephemeral Agent Memory (x402-paid)
Pay-per-write, free-to-read working memory for agents. Writes cost $0.01 (6h TTL) or $0.02 (12h TTL) in USDC on Base; reads are always free. Memories self-destruct at the storage layer when the TTL expires — no archives, no extensions, no manual cleanup.
## When to use this (vs. alternatives)
Use Memwry when you need state to:
- survive a process restart or crash mid-task
- pass between agents/frameworks with no shared infrastructure
- avoid re-paying for repeated LLM completions or upstream API calls within a time window
- expire automatically without you managing cleanup
Don't use it for anything that must persist past 12h, anything requiring confidentiality (see Privacy below), or anything needing queries/schema — it's key-value slots, not a database.
## Guardrails (read before integrating)
- **Reads are unauthenticated, payloads are plaintext.** Anyone who knows `agentId` + `key` can recall the memory during its TTL. Treat everything written as public for its lifetime. Encrypt client-side first if the data is sensitive, and share the decryption key out-of-band.
- **Idempotency-Key prevents double-charging on retry.** Always send one on writes if there's any chance of retry (crash, timeout, network blip) — 24h dedup window, replay returns `PAYMENT-RESPONSE: replayed` with no new charge.
- **Tier choice**: 6h for same-session/intraday work; 12h if the task may run overnight or the handoff receiver's timing is uncertain. Default to 6h unless you have a reason to pay more.
- **402 handling**: if payment never resolves (insufficient funds, wrong network, signature rejected), the write never lands — don't assume a POST succeeded without checking for a 200 + receipt.
- **No accounts, no API keys** — the x402 payment itself is the auth. There's nothing to configure ahead of time beyond the signer.
## API Endpoints
Base URL: `https://memwry.servitor.workers.dev`
**Write (paid):**
```
POST /mem/:agentId/:key/6h → $0.01 USDC, 6h TTL (21600s)
POST /mem/:agentId/:key/12h → $0.02 USDC, 12h TTL (43200s)
```
Headers: `Idempotency-Key` (recommended), `PAYMENT-SIGNATURE` (from x402 client)
Response Headers: `PAYMENT-RESPONSE`, `X-Receipt`, `X-Tier` (6h or 12h), `X-TTL-Seconds`, `X-Price-USDC`, `X-Expires-At`
Returns: Engram receipt — `txHash`, `paidAt`, `expiresAt`, `ttlSeconds`, `tier`, `amount`
**Read (free & public recall):**
```
GET /mem/:agentId/:key
```
Every GET endpoint is free and public (no payment, no authentication, no API keys). Returns the exact payload written, plus `X-Agent-ID`, `X-Expires-At`, `X-Tier`, `X-Price-USDC`, and `X-TTL-Seconds` headers. Rate limit: 100 req/hour/IP.
## Rate Limiting Policy & Key Structure
Unpaid API access (such as memory recall via `GET /mem/:agentId/:key`) is rate-limited at the Cloudflare Worker edge using a dedicated Cloudflare Durable Object (`RateLimiterDO`).
### The `rl:${ip}:${hour}` Key Structure
Each incoming unpaid request routes to an isolated Durable Object instance:
```
rl:${ip}:${hour}
```
- **`${ip}`**: Extracted from the client's verified connecting IP (`cf-connecting-ip` or first hop of `x-forwarded-for`).
- **`${hour}`**: The discrete UTC epoch hour window integer (`Math.floor(Date.now() / 3600000)`).
- **Atomic Serial Processing & Alarms**: Because each Durable Object instance processes requests serially, check-and-increment operations are strictly atomic, eliminating race conditions under burst traffic. Self-cleaning DO Alarms automatically clear hourly storage after 3600 seconds.
### How This Impacts User Requests
1. **Sliding Hourly Bucketing**: Requests are grouped into fixed 60-minute UTC blocks. At the start of a new hour, the key suffix rolls forward, granting a fresh quota of 100 requests.
2. **Independent of `agentId`**: Because rate limiting is tied to the physical caller's network IP rather than the target `agentId`, an agent or orchestrator cannot bypass limits by querying multiple agent IDs. All reads originating from the same machine share the 100 reads/hour budget.
3. **Multi-Tenant / Shared IP Impact**: Agents running behind shared NAT gateways, shared proxies, or shared cloud egress IPs will share the 100 req/hour window. For high-throughput architectures, ensure calling agents maintain distinct egress IPs or implement local in-memory caching.
4. **Rejection Response**: When a client IP attempts more than 100 reads within the active hour, the Worker immediately halts execution and returns:
```json
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{ "error": "rate_limit_exceeded", "limit": 100 }
```
5. **Paid Writes Are Not Bound to This Limit**: Paid memory storage (`POST`) is gated by x402 payment settlement rather than the unpaid read limit counter.
## Rate Limiting Reference
Use this reference table to configure agent polling intervals, retry backoff algorithms, and failover behavior across different operation tiers:
| Route / Tier | Cost & Protocol | Limit Scope | Window & Key Scheme | HTTP Error Code | Recommended Agent Retry Strategy |
|---|---|---|---|---|---|
| **Read (Recall)**
`GET /mem/:agentId/:key` | **Free & Public**
(No auth/payment) | **100 req / hour** per IP | 60-min UTC epoch block
`rl:${ip}:${hour}` (Durable Object) | `429 Too Many Requests` | Backoff with exponential jitter; pause polling until next UTC hour turn; implement local memory/LRU cache for repeated hot keys. |
| **Write (6h Tier)**
`POST /mem/:agentId/:key/6h` | **$0.01 USDC**
(x402 on Base) | No IP rate limit
(Fair-use payment gated) | 24-hour idempotency
`idem:${agentId}:${Idempotency-Key}` | `409 Conflict` (in progress)
`422` (reused mismatch) | Always supply same `Idempotency-Key` on retry. Retry immediately on network timeout; backoff 500ms–1s on 409. Never generates duplicate charges. |
| **Write (12h Tier)**
`POST /mem/:agentId/:key/12h` | **$0.02 USDC**
(x402 on Base) | No IP rate limit
(Fair-use payment gated) | 24-hour idempotency
`idem:${agentId}:${Idempotency-Key}` | `409 Conflict` (in progress)
`422` (reused mismatch) | Same as 6h tier. Ensure `Idempotency-Key` is retained across process restarts to guarantee exactly-once payment settlement. |
| **A2A Interface**
`POST /a2a` | Protocol defined
(A2A v1.0) | Standard gateway limits | Per-request idempotency
`Idempotency-Key` header | `400` (version/parse)
`32601` (method not found) | Send `A2A-Version: 1.0` and unique `Idempotency-Key`. On transient failures, retry with exponential backoff. |
## Payment flow (x402)
1. POST without payment → `402` with `PAYMENT-REQUIRED` header.
2. Client signs an EIP-3009 transfer authorization off-chain.
3. Retry with `PAYMENT-SIGNATURE` header.
4. Memwry verifies, settles on Base via the Coinbase facilitator, writes the value.
5. `200 OK` with `PAYMENT-RESPONSE: settled;txHash=0x...`, `X-Tier`, and base64 `X-Receipt`.
## Use Cases
### 1. Task State (Survive Restarts & Crashes)
**Use this when**: An agent executes long-running or multi-stage workflows where an unhandled exception, pod restart, or runtime timeout would lose intermediate progress. Write checkpoints before risky actions; recall on boot. Idempotency prevents double-billing on network retry.
```typescript
// Before executing a risky multi-step migration or external action:
const taskId = "task-sync-9481";
const checkpointKey = `checkpoint:${taskId}`;
const idempotencyKey = `idem_${taskId}_step3`;
// 1. Save checkpoint ($0.02 for 12h overnight resilience)
await payFetch(`https://memwry.servitor.workers.dev/mem/orchestrator/${checkpointKey}/12h`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify({
completedSteps: ["fetch_data", "transform_schema"],
currentStep: "load_destination",
cursor: "rec_9938102"
})
});
// 2. On worker restart or crash recovery:
const recoveryRes = await fetch(`https://memwry.servitor.workers.dev/mem/orchestrator/${checkpointKey}`);
if (recoveryRes.ok) {
const state = await recoveryRes.json();
console.log("Resuming from cursor:", state.cursor);
}
```
### 2. Session Memory (Cross-Request Ephemeral Context)
**Use this when**: Serverless or stateless worker agents process conversation turns across distributed instances without a persistent database. Store a rolling summary or working variables once, recall it on subsequent turns, and let storage-layer TTL handle automatic teardown.
```typescript
const sessionId = "sess_user_alpha_77";
// Save updated conversation memory turn ($0.01 for 6h intraday session)
await payFetch(`https://memwry.servitor.workers.dev/mem/agent-chatbot/session:${sessionId}/6h`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID()
},
body: JSON.stringify({
userIntent: "book_flight_paris",
slots: { destination: "CDG", departDate: "2026-10-15", passengers: 2 },
lastInteraction: new Date().toISOString()
})
});
// Free recall on the next incoming chat webhook:
const sessionRes = await fetch(`https://memwry.servitor.workers.dev/mem/agent-chatbot/session:${sessionId}`);
const sessionData = sessionRes.ok ? await sessionRes.json() : null;
```
### 3. Scratch Space (Offload Large Context Window Artifacts)
**Use this when**: Raw payloads, bulky tool outputs, scraped markdown, or intermediate code trees exceed model context limits. Park the raw bytes under a scratch key, keep only a compact reference pointer in the LLM prompt, and fetch sections on-demand.
```typescript
const runId = "run_audit_01";
const rawHtmlPayload = "... large 200KB web page dump ...";
// Park raw artifact in scratch space ($0.01 for 6h TTL)
await payFetch(`https://memwry.servitor.workers.dev/mem/researcher/scratch:${runId}:page/6h`, {
method: "POST",
headers: {
"Content-Type": "text/html",
"Idempotency-Key": `idem_scratch_${runId}`
},
body: rawHtmlPayload
});
// Subsequent tool execution retrieves specific stored artifact free:
const docRes = await fetch(`https://memwry.servitor.workers.dev/mem/researcher/scratch:${runId}:page`);
const rawHtml = await docRes.text();
```
### 4. LLM Response Cache (Stop Re-Paying for Identical Reasoning)
**Use this when**: Multiple agents or repeated requests query deterministic reasoning, complex synthetic extractions, or code evaluations. Key the slot by a SHA-256 hash of the model + prompt + temperature tuple. Replacing repeated $0.12 LLM inference with free reads yields >90% cost reduction.
```typescript
import { createHash } from "node:crypto";
const prompt = "Summarize the technical spec of RFC 9457 Problem Details";
const promptHash = createHash("sha256").update(`gemini-2.5-flash|${prompt}`).digest("hex");
const cacheKey = `cache:${promptHash}`;
// Check cache first (free read)
const cached = await fetch(`https://memwry.servitor.workers.dev/mem/synthesizer/${cacheKey}`);
if (cached.ok) {
const result = await cached.text();
console.log("Cache hit (free recall):", result);
} else {
// Compute via LLM and cache for 6 hours ($0.01 write)
const answer = "RFC 9457 defines a 'problem detail' as a way to carry machine-readable error details...";
await payFetch(`https://memwry.servitor.workers.dev/mem/synthesizer/${cacheKey}/6h`, {
method: "POST",
headers: { "Content-Type": "text/plain", "Idempotency-Key": `idem_${promptHash}` },
body: answer
});
}
```
### 5. Multi-Agent Handoff (Decoupled Ephemeral State Relay)
**Use this when**: Agent A finishes its stage (e.g., discovery or planning) and must hand execution off to Agent B (e.g., code writer or auditor) without managing shared message queues, databases, or long-term credentials. The memory key string acts as the coordination handle.
```typescript
// AGENT A (Planner) writes handoff payload:
const handoffChannel = "handoff:batch-3810";
await payFetch(`https://memwry.servitor.workers.dev/mem/shared-pipeline/${handoffChannel}/12h`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": "idem_handoff_batch_3810"
},
body: JSON.stringify({
assignedTo: "agent-coder-9",
actionPlan: ["implement_types", "write_tests", "deploy_worker"],
specVersion: "v2.1"
})
});
// AGENT B (Executor) polls and recalls handoff artifact for free:
const handoffRes = await fetch(`https://memwry.servitor.workers.dev/mem/shared-pipeline/${handoffChannel}`);
if (handoffRes.ok) {
const instructions = await handoffRes.json();
console.log("Received handoff for:", instructions.assignedTo);
}
```
### 6. Upstream API Cache (Rate-Limit & Third-Party Cost Shield)
**Use this when**: An agent queries expensive or strictly rate-limited third-party APIs (such as market data, chain indexers, or proprietary weather feeds). Shield upstream quotas and absorb concurrent traffic spikes through Memwry's edge memory.
```typescript
const symbol = "ETH_USDC";
const apiCacheKey = `apicache:dex:${symbol}`;
// Free read check against edge memory
const cacheRes = await fetch(`https://memwry.servitor.workers.dev/mem/dex-oracle/${apiCacheKey}`);
if (cacheRes.ok) {
const quote = await cacheRes.json();
console.log("Cached DEX quote:", quote.price);
} else {
// Fetch from expensive upstream provider, then cache for 6 hours
const upstreamQuote = { price: 3412.50, timestamp: Date.now() };
await payFetch(`https://memwry.servitor.workers.dev/mem/dex-oracle/${apiCacheKey}/6h`, {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": `idem_dex_${symbol}_${Date.now()}` },
body: JSON.stringify(upstreamQuote)
});
}
```
## TypeScript client
```typescript
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
client.registerScheme(new ExactEvmScheme(signer));
const payFetch = wrapFetchWithPayment(fetch, client);
// Write: $0.01, 6h
const postRes = await payFetch("https://memwry.servitor.workers.dev/mem/agent-1/goal/6h", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ goal: "analyze dataset", currentStep: 2 }),
});
const engram = await postRes.json();
// Read: free
const memRes = await fetch("https://memwry.servitor.workers.dev/mem/agent-1/goal");
const memory = await memRes.json();
```