Checkpoint.

A Checkpoint is a lease for "hold this for me." You drop a session output or state snapshot in, walk away, come back later, pull it down. Retrieval triggers deletion. Useful when your agent is about to terminate and needs to park something for its next incarnation (or for a successor) to pick up.

That's the shortcut. Under the hood, a Checkpoint is a Lease keyed by a workflow_id you pick, reserving a bounded slot of JSON state. The v1 backend treats the slot as a mutable, versioned key/value cell — you can overwrite it as many times as you want inside the paid capacity, and you can read it repeatedly. The clean "park once, pull once" pattern above is the typical way to use it; the cell just happens to support more.

Use Checkpoint when you want the shortcut. If you need the explicit file semantics or the multi-party handoff affordance, Storage is the primitive.

How the backend implements this shortcut today

Create a Checkpoint

Pay once. The initial request body becomes the first version of the stored state. X-Workflow-Id is your key; X-Max-Body-Bytes caps the reserved slot (defaults and ceilings come from system config).

curl
Node
Python
# 256 KB slot for 1 hour, keyed by "crawl-job-42"
curl -X POST https://relaystation.ai/api/checkpoint \
  -H "Authorization: Bearer rs_live_YOUR_KEY" \
  -H "X-Workflow-Id: crawl-job-42" \
  -H "X-Duration-Seconds: 3600" \
  -H "X-Max-Body-Bytes: 262144" \
  -H "Content-Type: application/json" \
  -d '{"cursor": 0, "visited": []}'
const state = { cursor: 0, visited: [] };
const res = await fetch('https://relaystation.ai/api/checkpoint', {
  method: 'POST',
  headers: {
    'Authorization':       `Bearer ${process.env.RS_KEY}`,
    'X-Workflow-Id':       'crawl-job-42',
    'X-Duration-Seconds':  '3600',
    'X-Max-Body-Bytes':    '262144',
    'Content-Type':        'application/json',
  },
  body: JSON.stringify(state),
});
console.log(await res.json());
state = {'cursor': 0, 'visited': []}

res = requests.post(
    'https://relaystation.ai/api/checkpoint',
    headers={
        'Authorization':       f'Bearer {os.environ["RS_KEY"]}',
        'X-Workflow-Id':       'crawl-job-42',
        'X-Duration-Seconds':  '3600',
        'X-Max-Body-Bytes':    '262144',
        'Content-Type':        'application/json',
    },
    json=state,
)
print(res.json())

Read the current state

The response body is the latest version as a raw JSON blob. Version and last-written timestamp come back in response headers so you can compare-and-set on the next write.

curl
Node
Python
curl -i https://relaystation.ai/api/checkpoint/crawl-job-42 \
  -H "Authorization: Bearer rs_live_YOUR_KEY"
const res = await fetch(
  'https://relaystation.ai/api/checkpoint/crawl-job-42',
  { headers: { Authorization: `Bearer ${process.env.RS_KEY}` } },
);
const version = res.headers.get('x-checkpoint-version');
const state   = await res.json();
res = requests.get(
    'https://relaystation.ai/api/checkpoint/crawl-job-42',
    headers={'Authorization': f'Bearer {os.environ["RS_KEY"]}'},
)
version = res.headers['X-Checkpoint-Version']
state   = res.json()

Update the state

A PUT overwrites the body. No additional payment — the initial lease reservation covers writes up to max_body_bytes. The version counter increments automatically; read it back in X-Checkpoint-Version.

curl
Node
Python
curl -X PUT https://relaystation.ai/api/checkpoint/crawl-job-42 \
  -H "Authorization: Bearer rs_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cursor": 128, "visited": ["a", "b"]}'
await fetch(
  'https://relaystation.ai/api/checkpoint/crawl-job-42',
  {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${process.env.RS_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ cursor: 128, visited: ['a', 'b'] }),
  },
);
requests.put(
    'https://relaystation.ai/api/checkpoint/crawl-job-42',
    headers={
        'Authorization': f'Bearer {os.environ["RS_KEY"]}',
        'Content-Type': 'application/json',
    },
    json={'cursor': 128, 'visited': ['a', 'b']},
)

Delete early

The clean "pull it down and be done" flow doesn't require an explicit delete — lease expiry sweeps the slot regardless. But if your agent finishes before expiry and wants the capacity gone immediately:

curl
curl -X DELETE https://relaystation.ai/api/checkpoint/crawl-job-42 \
  -H "Authorization: Bearer rs_live_YOUR_KEY"

No refund is issued for early delete at v1 — you paid for the reservation, not for the occupancy.

Pricing

Checkpoint uses reservation pricing: you pay max_body_bytes × duration × rate at creation and write as many versions as you want inside that capacity, for free. See GET /api/pricing for the live rate.

Handing off a Checkpoint lease

Coming soon. Claim-token semantics for Checkpoint are in design; POST /api/handoff/:lease_id is wired for Storage only today.

If your agent wants to park a state snapshot for another agent to pick up right now, the direct path is: wrap the snapshot in a Storage lease with delete_on_claim: true, and hand off the Storage lease. The successor agent redeems the claim URL once and gets the bytes — same one-shot flavor you'd want from a Checkpoint handoff, on a lease variant where handoff is already shipped.

Checkpoint-native handoff is tracked in the changelog.

Expiry behavior

At expires_at, the current body and every prior version are deleted. No grace period, no way to extend. Reads after expiry return 410 Gone.

Back to
Leases — the primitive