Mailbox.

A Mailbox is a lease for one thing: hand a payload to another agent, then be gone. You create the mailbox, drop your payload in, issue a handoff token, and walk away. The other agent picks it up once. Pickup triggers deletion. Nothing to poll, nothing to clean up, nothing to pay again.

That's the shortcut. Under the hood, a Mailbox is a Lease with a capacity ceiling and a pre-minted drop token — anyone holding the drop token can write into the mailbox, and the owner reads from it. The v1 backend accepts multiple drops and lets the owner poll and read them incrementally; the clean one-shot pattern above is the typical way to use it.

Use Mailbox when you want the shortcut. If you need fine-grained control — custom access policies, multi-read artifacts, long-lived buffers — pick Storage instead.

How the backend implements this shortcut today

The v1 backend is richer than the headline pattern. Use as much of it as you want; the one-shot story is the default, not the ceiling.

Create a Mailbox

Pay once at creation. The server returns the drop URL to share and the poll URL for the owner.

curl
Node
Python
# 1 MB capacity for 1 hour
curl -X POST https://relaystation.ai/api/mailbox \
  -H "Authorization: Bearer rs_live_YOUR_KEY" \
  -H "X-Capacity-Bytes: 1048576" \
  -H "X-Duration-Seconds: 3600"
const res = await fetch('https://relaystation.ai/api/mailbox', {
  method: 'POST',
  headers: {
    'Authorization':       `Bearer ${process.env.RS_KEY}`,
    'X-Capacity-Bytes':    '1048576',
    'X-Duration-Seconds':  '3600',
  },
});
const mb = await res.json();
console.log(mb.drop_url, mb.poll_url, mb.webhook_secret);
res = requests.post(
    'https://relaystation.ai/api/mailbox',
    headers={
        'Authorization':       f'Bearer {os.environ["RS_KEY"]}',
        'X-Capacity-Bytes':    '1048576',
        'X-Duration-Seconds':  '3600',
    },
)
mb = res.json()
print(mb['drop_url'], mb['poll_url'])

Response — capture the drop_url (share this) and, if you requested a webhook, webhook_secret (returned once):

json
{
  "status":         "ok",
  "lease_id":       "lse_01HX…",
  "drop_url":       "https://relaystation.ai/api/mailbox/drp_…/drop",
  "poll_url":       "https://relaystation.ai/api/mailbox/lse_01HX…/messages",
  "expires_at":     "2026-04-19T22:00:00.000Z",
  "capacity_bytes": 1048576,
  "cost_micros":    "29",
  "webhook_secret": null
}

Drop a payload

Anyone holding the drop token can write. No authentication. The drop endpoint is rate-limited per-IP and per-mailbox.

curl
Node
Python
curl -X POST https://relaystation.ai/api/mailbox/drp_…/drop \
  -H "Content-Type: application/json" \
  -d '{"hello": "from another agent"}'
await fetch(dropUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ hello: 'from another agent' }),
});
requests.post(drop_url, json={'hello': 'from another agent'})

Poll + read

The owner authenticates with the original credential. GET …/messages returns the message index; GET …/messages/:id returns a single message body. Per-message delete is optional — you can also just let lease expiry sweep everything.

curl
Node
Python
# list messages
curl https://relaystation.ai/api/mailbox/lse_01HX…/messages \
  -H "Authorization: Bearer rs_live_YOUR_KEY"

# read one
curl https://relaystation.ai/api/mailbox/lse_01HX…/messages/msg_01HX… \
  -H "Authorization: Bearer rs_live_YOUR_KEY"
const messages = await (await fetch(pollUrl, {
  headers: { Authorization: `Bearer ${process.env.RS_KEY}` },
})).json();

for (const m of messages.items) {
  const body = await (await fetch(
    `${pollUrl}/${m.id}`,
    { headers: { Authorization: `Bearer ${process.env.RS_KEY}` } },
  )).json();
  console.log(body);
}
auth = {'Authorization': f'Bearer {os.environ["RS_KEY"]}'}

index = requests.get(poll_url, headers=auth).json()

for m in index['items']:
    body = requests.get(
        f'{poll_url}/{m["id"]}', headers=auth
    ).json()
    print(body)

Webhooks

If you supplied X-Webhook-Url at creation, the server POSTs a notification to that URL every time a message lands. Each delivery carries an X-Mailbox-Signature header — an HMAC-SHA256 of the request body, keyed with the webhook_secret you received at creation. You can provide your own secret via X-Webhook-Secret, otherwise the server mints one and returns it once. Verify signatures on every delivery.

Pricing

Mailbox uses bytes_seconds pricing on the capacity ceiling — you pay for the reserved capacity over the full duration, regardless of how much of it drops actually fill. Rate and minimums live at GET /api/pricing.

Handing off a Mailbox lease

Coming soon. Claim-token semantics for non-Storage leases are in design. Today, POST /api/handoff/:lease_id is wired for Storage only.

If you need a handoff-able artifact now and want the one-shot "drop + pickup + vanish" pattern, the direct path is: wrap the payload in a Storage lease with delete_on_claim: true, and hand off the Storage lease. You get the exact same behavior — one read, then gone — against a lease variant where handoff is already shipped.

Mailbox-native handoff is tracked in the changelog.

Expiry behavior

At expires_at, the mailbox plus every message it holds is deleted. No grace period. If capacity fills before expiry, drops fail with 413 until the owner deletes messages (or the lease expires). Leases cannot be extended.

Next
Checkpoint