It's still there when you're not. Storage, Mailbox, Checkpoint — same primitive, different shortcut. Configure your own or call one of ours.
Think of it like a parking meter. You pay for a spot, for a defined time, then it expires automatically. Every lease works the same way — the meter just holds a different kind of space. Lease it. Pay for it. Done.
Every variant is a lease — pre-configured for a common workflow. Call the shortcut that fits, or configure your own.
Size, duration, access rules — you pick all three. Multi-read, multi-party handoff, long-lived buffers, anything the shortcuts don't fit.
Storage docs →Create. Drop your payload. Issue a token. Walk away. The other agent picks it up once; pickup triggers deletion. Nothing to poll, nothing to clean up.
Mailbox docs →Drop a session output or state snapshot. Come back later — or have your successor come back — and pull it down. Retrieval triggers deletion.
Checkpoint docs →More lease variants coming — Scratchpad (short-lived working memory), Cron (lease + scheduler).
Discover and quote are optional. If you already know the rate, skip straight to the create call — one POST and you're done.
POST to the variant endpoint (/api/store, /api/mailbox, or /api/checkpoint) with an x402 payment header or an rs_live_ API key, and the lease parameters in request headers. Receipt comes back in the response — lease_id plus the variant-specific fields you'll use for the rest of the lease's life.
Hit /api/pricing once for the live rate, wallet address, and endpoint list. Cache it — it rarely changes.
Call /api/quote for an exact price. Or calculate yourself: (bytes/1GB) × (seconds/86400) × $0.001. Same formula for every variant; Checkpoint uses max_body_bytes instead of actual file size.
One POST — /api/store, /api/mailbox, or /api/checkpoint — with payment and parameters. Atomic: if the server can't write the lease, the charge refunds automatically.
Write the lease_id and expires_at. The underlying resource deletes itself when the lease expires — to the second. No cleanup call.
No SDK. No IAM. No billing account. Just HTTP. Pick the variant below — the shape of the call is the same; the headers tell the server which shortcut you want.
// ── OPTIONAL: discover pricing once and cache it ───────────────────────── const pricing = await fetch('https://relaystation.ai/api/pricing').then(r => r.json()); // → { rateUsdcPerGbDay: 0.001, walletAddress: '0x...', minDurationSeconds: 60 } // ── OPTIONAL: get an exact quote ───────────────────────────────────────── const quote = await fetch( `https://relaystation.ai/api/quote?size_bytes=${fileBytes}&duration_seconds=3600` ).then(r => r.json()); // → { priceUsdc: 0.01, expiresAt: '...' } // ── REQUIRED: pay and create the Storage lease ──────────────────────────── const receipt = await fetch('https://relaystation.ai/api/store', { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}`, // or X-Payment via x402 'X-File-Name': 'report.pdf', 'X-Size-Bytes': String(fileBytes), 'X-Duration-Seconds': '3600', 'Content-Type': 'application/pdf', }, body: fileStream, }); // → { lease_id, object_key, expires_at, duration_human, paid_usdc }
// ── OPTIONAL: discover + quote ─────────────────────────────────────────── const pricing = await fetch('https://relaystation.ai/api/pricing').then(r => r.json()); const quote = await fetch( `https://relaystation.ai/api/quote?size_bytes=1048576&duration_seconds=3600` ).then(r => r.json()); // ── REQUIRED: pay and create the Mailbox lease ──────────────────────────── const mb = await fetch('https://relaystation.ai/api/mailbox', { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}`, // or X-Payment via x402 'X-Capacity-Bytes': '1048576', // 1 MB reservation 'X-Duration-Seconds': '3600', }, }).then(r => r.json()); // → { lease_id, drop_url, poll_url, expires_at, capacity_bytes } // Hand mb.drop_url to whoever needs to deliver. No auth on drop. // Pick up with GET mb.poll_url (authed as the owner).
// ── OPTIONAL: discover + quote ─────────────────────────────────────────── const pricing = await fetch('https://relaystation.ai/api/pricing').then(r => r.json()); // ── REQUIRED: pay and create the Checkpoint lease ───────────────────────── const cp = await fetch('https://relaystation.ai/api/checkpoint', { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}`, // or X-Payment via x402 'X-Workflow-Id': 'crawl-job-42', 'X-Duration-Seconds': '3600', 'X-Max-Body-Bytes': '262144', // 256 KB reservation 'Content-Type': 'application/json', }, body: JSON.stringify({ cursor: 0, visited: [] }), }).then(r => r.json()); // → { lease_id, workflow_id, expires_at, max_body_bytes } // Overwrite anytime with PUT /api/checkpoint/crawl-job-42 — no extra charge.
Each variant competes with different infrastructure, and the pitch against each is the same idea expressed three ways: no account, pay once, expires itself.
A Mailbox is a lease for one thing: create, drop your payload, issue a token, walk away. The other agent picks it up once. Pickup triggers deletion. Nothing to poll, nothing to clean up, nothing to pay again.
Same primitive as Storage, pre-configured for async inter-agent handoff. The backend also supports multi-drop + polling for richer workflows — the shortcut above is the default, not the ceiling.
POST /api/mailbox with X-Capacity-Bytes and X-Duration-Seconds. Returns a drop_url and a poll_url.
Share the drop_url. Anyone holding it can POST a payload — no auth on the drop endpoint, the URL IS the credential.
GET poll_url for the message index, GET a single message to read it. In the one-shot shortcut, this is also the last call.
Every queued message and the mailbox itself are physically deleted at expires_at. No cron, no retention sweep — expiry is the cleanup.
// Agent A — pay once for 1 MB capacity, 1 hour const mb = await fetch('https://relaystation.ai/api/mailbox', { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}`, 'X-Capacity-Bytes': '1048576', 'X-Duration-Seconds': '3600', }, }).then(r => r.json()); // → { drop_url, poll_url, expires_at, capacity_bytes, cost_micros } // Hand drop_url to whoever needs to deliver — no auth on drop await fetch(mb.drop_url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ task: 'review contract-42.pdf' }), }); // Agent B — pick up (one-shot) const messages = await fetch(mb.poll_url, { headers: { 'Authorization': `Bearer ${rs_live_key}` }, }).then(r => r.json()); // → { items: [{ id, size_bytes, received_at }, ...] }
Storage file, Mailbox drop, Checkpoint snapshot — once you have a lease, you can turn it into a sealed claim URL that another agent redeems without authentication. The token IS the credential. Handoff is free at v1 defaults; you pay once for the underlying lease.
A self-destructing sealed envelope that fits any lease variant. Agent A has a lease — storage, mailbox, or checkpoint — and seals it. Agent B receives the claim URL and opens it. The envelope is gone. No shared backend. No trust required between agents.
Any variant. Storage lease_id, mailbox lease_id, checkpoint lease_id. Same /api/handoff/{id} endpoint regardless.
POST /api/handoff/{lease_id} with max_claims, delete_on_claim, ttl_seconds. Get back a claim_url.
Pass the claim_url through any channel — message queue, mailbox drop, webhook, shared context. The URL itself is the credential.
GET the claim_url — no auth, no wallet, no setup. The underlying resource streams back. If delete_on_claim, the lease is physically deleted on the final claim.
// Agent A — after creating a Storage lease, issue a handoff token const handoff = await fetch(`https://relaystation.ai/api/handoff/${storage_lease_id}`, { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}` }, body: JSON.stringify({ max_claims: 1, delete_on_claim: true, ttl_seconds: 3600, }), }).then(r => r.json()); // → { claim_url, handoff_token, expires_at, lease_expires_at } // Agent B — redeem, no auth required. Bytes stream back with original filename. const file = await fetch(handoff.claim_url);
// Handoff for Mailbox + Checkpoint — rolling out through the claim-semantics // work in the changelog. Today, the bridging pattern is: // 1. Package the payload as a Storage lease with delete_on_claim: true const bridge = await fetch('https://relaystation.ai/api/store', { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}`, 'X-File-Name': 'mailbox-payload.json', 'X-Size-Bytes': String(payloadBytes), 'X-Duration-Seconds': '3600', 'Content-Type': 'application/json', }, body: payload, }).then(r => r.json()); // 2. Hand off the Storage lease — same claim flow, same URL shape const handoff = await fetch(`https://relaystation.ai/api/handoff/${bridge.lease_id}`, { method: 'POST', headers: { 'Authorization': `Bearer ${rs_live_key}` }, body: JSON.stringify({ max_claims: 1, delete_on_claim: true }), }).then(r => r.json()); // Agent B claims the bridge lease as usual.
// Checkpoint handoff follows the same bridging pattern as Mailbox today: // wrap the latest state snapshot in a Storage lease with delete_on_claim: true, // hand off the Storage lease. Native Checkpoint handoff lands when claim // semantics for non-Storage leases finalise (track the changelog). // 1. Read the current checkpoint state const res = await fetch(`https://relaystation.ai/api/checkpoint/${workflow_id}`, { headers: { 'Authorization': `Bearer ${rs_live_key}` }, }); const state = await res.json(); // 2. Wrap + hand off as a Storage lease const bridge = await fetch('https://relaystation.ai/api/store', { /* ... */ }).then(r => r.json()); const handoff = await fetch(`https://relaystation.ai/api/handoff/${bridge.lease_id}`, { /* ... */ });
max_claims — how many times the token can be redeemed (default 1) ·
delete_on_claim — physically delete the underlying resource after the final claim (default false) ·
ttl_seconds — token expiry, clamped to the lease's own expiry (default 3600)
DELETE /api/handoffs/:id invalidates an outstanding token. Idempotent — the second call returns 404. Claims already completed stand.
Rate: $0.001 / GB / day. Same formula across every variant. You pay once at creation — no subscription, no renewal. Prices that hit the network minimum are shown in green.
POST /api/handoff/:lease_id) and redeeming one (GET /api/claim/:token) are $0 at v1 defaults. You pay once for the underlying lease; every handoff off that lease is free.
Every design decision optimises for autonomous operation with zero human involvement.
Every variant is advertised at /.well-known/x402.json and /api/pricing. Rates, minimums, canonical endpoint list — no docs-scraping required.
Wallet signature via x402 for ephemeral agents, prepaid balance via rs_live_ API key for long-running ones, account JWT for human dashboard sessions. Same endpoints, caller picks the path.
Whatever the variant — Storage file, Mailbox messages, Checkpoint versions — the underlying resource is physically deleted at the lease's expires_at. Not day-granularity, not best-effort.
Any lease can be handed off. Same /api/handoff/:lease_id endpoint for Storage, Mailbox, or Checkpoint. Token IS the credential; redeeming agent needs no auth. Free at v1 defaults.
Storage is the configurable primitive. Mailbox and Checkpoint are pre-configured shortcuts for common agent patterns. Scratchpad + Cron variants coming. Learn one, you've learned them all.
Every variant is backed by AWS S3. 99.999999999% durability. No blockchain storage, no probabilistic guarantees. The bytes are where you'd expect them.
Three variant groups plus cross-cutting Features and Discovery. All endpoints return JSON; body-carrying creation endpoints accept raw bytes (Storage) or JSON (Mailbox drop, Checkpoint create/write). The full OpenAPI spec lives at docs.relaystation.ai/reference/openapi.html.
X-File-Name, X-Size-Bytes, X-Duration-Seconds. Auth: X-Payment (x402) or Authorization: Bearer rs_live_…. Returns { lease_id, expires_at, duration_human, size_bytes, paid_usdc, … }.410 Gone if the lease has expired.X-Capacity-Bytes, X-Duration-Seconds. Optional: X-Webhook-Url, X-Webhook-Secret, X-Max-Message-Bytes, X-Max-Messages. Returns { lease_id, drop_url, poll_url, webhook_secret, capacity_bytes, expires_at }. Webhook secret returned exactly once.DELETE on the same path removes one message; DELETE /api/mailbox/{id} removes the whole mailbox early.X-Workflow-Id (^[A-Za-z0-9._-]{1,128}$), X-Duration-Seconds. Optional: X-Max-Body-Bytes (default from config). Body = initial JSON state. Returns { lease_id, workflow_id, max_body_bytes, expires_at }.X-Checkpoint-* response headers.max_body_bytes. Version counter auto-increments.max_claims (default 1), delete_on_claim (default false), ttl_seconds (default 3600). Returns { handoff_token, claim_url, expires_at, lease_expires_at }.404. Claims already completed stand.delete_on_claim, removes the lease after the final claim./.well-known/x402.json.(bytes/1073741824) × (seconds/86400) × 0.001.