The three persistence tiers
Every file-taking or file-producing op on Relaystation uses the same I/O model, with three tiers. You pick a tier per call by how you pass (or receive) the bytes — there is no setup step and no required storage product.
| Tier | What it is | Cost | Lifetime | Scope |
|---|---|---|---|---|
| Inline | base64 in the request/response body | free (part of the call) | none — nothing persists | the one call |
| Scratch | customer-scoped working storage | free | 24 hours | you only — reusable across calls |
| Baton | the durable storage product | paid (quoted at create) | what you configure | shareable, witnessed, durable |
Compute is priced per op on the metered input regardless of which tier the bytes came from. Scratch costs nothing; storage and egress economics live in the baton tier alone.
Inline — small files, zero ceremony
Up to 4 MiB (decoded), pass { "inline": "<base64>" } as the op’s file field and get small outputs back the same way: { "output": { "inline": "<base64>", "sizeBytes": ..., "contentType": ... } }. Nothing is stored; there is nothing to clean up. (One asymmetry, stated honestly: the output side gates on the base64-encoded size against the same 4 MiB threshold — base64 inflates ~1.37×, so raw outputs above ~3 MiB come back as a scratch reference instead. Handle both shapes and the difference never bites; see receiving outputs.)
Scratch — the free working tier
Over 4 MiB (or whenever you’d rather upload once and reuse), use scratch:
POST /v1/cputools/upload-url(free) returns a presigned upload form and aninputKeylikescratch/<your-customer-id>/<uuid>.- Upload your file (up to 50 MB) with one multipart POST to that URL.
- Pass
{ "inputKey": "..." }as any op’s file field — as many ops, as many times as you like, for 24 hours.
Scratch keys are namespaced to your customer identity. Another customer’s key — even if leaked — resolves to 404 for you, and yours for them. The same gate admits all three auth modes: API key, wallet JWT, or a signed x402 payment (so an account-less agent can use scratch too).
When an op’s output exceeds the inline threshold (measured on the encoded size — raw bytes above ~3 MiB), it lands in your scratch automatically and the response carries both handles:
{ "output": { "outputKey": "scratch/<you>/<uuid>", "outputUrl": "https://…presigned, 1 hour…", "sizeBytes": 12023329, "contentType": "image/png" } }
outputUrl is for downloading now. outputKey is for chaining.
Chaining: outputKey is an inputKey
An op’s outputKey lives in the same namespace as your uploads, so you can feed it straight into the next op’s inputKey — no download, no re-upload, no pipeline product:
# 1. Upload once (12 MB scan)
curl -s -X POST https://api.relaystation.ai/v1/cputools/upload-url \
-H "Authorization: Bearer $KEY" -d '{"ext":"png"}'
# → { "inputKey": "scratch/<you>/aaaa.png", "url": ..., "fields": {...} }
# (multipart-POST the file to url+fields)
# 2. First op — output exceeds 4 MiB, so it lands in scratch
curl -s -X POST https://api.relaystation.ai/v1/image/convert \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-d '{"file":{"inputKey":"scratch/<you>/aaaa.png"},"format":"png"}'
# → { "output": { "outputKey": "scratch/<you>/bbbb", ... } }
# 3. Chain — the outputKey IS the next inputKey
curl -s -X POST https://api.relaystation.ai/v1/image/metadata \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-d '{"file":{"inputKey":"scratch/<you>/bbbb"}}'
# → { "metadata": { "width": 2000, "height": 2000, "format": "png", ... } }
Each step is its own pay-per-call op with its own receipt. The bytes never leave the platform between steps, and the 24-hour scratch window comfortably covers a working session. (For chaining the tabular ops — filter, sort, join, SQL — in a single call, see POST /v1/pipeline, which threads bytes step-to-step and bills the sum.)
Baton — when the result needs to outlive the day
Scratch is working storage: free, yours, gone in 24 hours. When an output needs to be durable (kept past today), shareable (handed to another agent or a human via a token), or witnessed (tamper-evident, provable), that is the baton tier — a paid product with its own quoted price, lifecycle, and trust options.
Batons are optional, always. No op requires one; the lodestone path — one call, one payment, result back — never gains a mandatory storage step.