Portal
Documentation: all sections

Sable Legacy

A Legacy plan is a sealed note and files that release to named beneficiaries when you stop checking in. You pick a check-in cadence and a grace window; optionally you name attesters who must confirm before anything releases. Each beneficiary is a wallet, and each receives a one-time claim link in advance. The link does nothing until the plan releases; then the beneficiary signs in with that exact wallet and the content is opened for them.

Every check-in, warning, confirmation, trigger, release, claim and cancellation is appended to a per-plan hash chain and anchored publicly by the same worker that anchors the Vault and agent runs. The timeline — when you last checked in, when the warning fired, who confirmed — is provable by anyone holding the event list, and the events carry nothing about the content.

What is and is not guaranteed

Say this out loud before relying on it:

The state machine

active ──(check-in deadline passes)──▶ warning ──(grace passes AND
  ▲                                      │         confirmations ≥ required)
  └────────── owner checks in ───────────┘                │
                                                          ▼
                                            triggered ──▶ released

any state but cancelled ──(owner cancels)──▶ cancelled
StateMeaningWhat moves it
activeYou are checking in on time.The deadline (last_checkin_at + checkin_interval_secs) passes → warning.
warningA check-in was missed. Grace is running; attesters may now confirm.Grace passes and confirmations ≥ required → released. A check-in → active, clearing every confirmation.
triggeredTransitional; the same sweep that triggers also releases.Immediately released.
releasedBeneficiaries can open the content with their wallet.Cancel destroys the content (a switch that misfired can be pulled).
cancelledCiphertext destroyed, claim links dead.Terminal.

The sweeper runs once a minute on the background-task leader and re-decides every transition under a row lock, so a check-in that lands a moment before the deadline always wins.

Quickstart

Create a plan that expects a check-in every week, waits a day after a miss, and requires one of two attesters:

curl https://api.buildsable.com/v1/legacy/plans \
-H "Authorization: Bearer $SABLE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
  "name": "If I go quiet",
  "checkin_interval_secs": 604800,
  "grace_secs": 86400,
  "beneficiaries": [
    { "wallet": "0x1111111111111111111111111111111111111111", "label": "sister" }
  ],
  "attesters": [
    { "wallet": "0x2222222222222222222222222222222222222222", "label": "lawyer" },
    { "wallet": "0x3333333333333333333333333333333333333333", "label": "partner" }
  ],
  "attesters_required": 1,
  "note": "The hardware wallet is in the grey safe.",
  "files": [
    { "name": "recovery.txt", "content_type": "text/plain", "content_b64": "LS0tLS1CRUdJTi…" }
  ]
}'
{
  "plan": {
    "id": "lg_5f1c…",
    "name": "If I go quiet",
    "status": "active",
    "checkin_interval_secs": 604800,
    "grace_secs": 86400,
    "attesters_required": 1,
    "last_checkin_at": "2026-09-06T10:00:00Z",
    "next_deadline_at": "2026-09-13T10:00:00Z",
    "client_encrypted": false,
    "beneficiary_count": 1,
    "attester_count": 2,
    "confirmations": 0,
    "object_count": 1
  },
  "claim_urls": [
    {
      "wallet": "0x1111111111111111111111111111111111111111",
      "label": "sister",
      "url": "https://buildsable.com/legacy/claim/…"
    }
  ],
  "note": "Claim URLs are shown once and stored only as hashes…",
  "custody": "Sable holds this plan's note and files sealed under its own master key…"
}

The claim_urls are shown once — the gateway stores only a sha256 of each token. Hand each link to its beneficiary now. Then keep checking in:

curl -X POST https://api.buildsable.com/v1/legacy/plans/lg_5f1c…/checkin \
  -H "Authorization: Bearer $SABLE_SESSION_TOKEN"

A check-in resets the deadline, clears any warning, and clears every attester confirmation.

Endpoints

Owner and party endpoints are session-authed (Authorization: Bearer sess_…). The claim-link resolver is public, because a beneficiary holds only the link.

MethodPathAuthWhat it does
POST/v1/legacy/plansSessionCreate a plan. Returns the plan and the one-time claim_urls.
GET/v1/legacy/plansSessionYour plans: status, timers, counts (metadata only).
GET/v1/legacy/plans/{id}SessionDetail: parties, object metadata, and the chained events with anchor state. Never content.
POST/v1/legacy/plans/{id}/checkinSessionCheck in: resets the deadline, clears warning and confirmations, chains checkin.
PUT/v1/legacy/plans/{id}/contentSessionReplace the note and files; re-sealed; chains content_updated. Only while active or warning.
POST/v1/legacy/plans/{id}/cancelSessionCancel: nulls every ciphertext, kills every claim token, chains cancelled. Idempotent.
POST/v1/legacy/plans/{id}/attestSessionConfirm as an attester (one of your linked wallets must be named). Only counts while warning; idempotent per wallet.
GET/v1/legacy/attestingSessionPlans where one of your wallets is an attester: id, name, status, deadline, and whether you confirmed. Nothing else.
GET/v1/legacy/claimsSessionReleased plans naming one of your wallets as a beneficiary.
POST/v1/legacy/claims/{plan_id}/openSessionOpen a release: the note (unsealed in-frame) and the object list. Chains claimed once per beneficiary.
GET/v1/legacy/claims/{plan_id}/objects/{object_id}SessionOne file's bytes, base64.
GET/v1/legacy/claim/{token}PublicWhat a claim link reveals: {plan_name, wallet_required, status: "waiting" | "released"}. Unknown and cancelled tokens are a uniform 404.

A non-attester calling attest, a non-beneficiary calling open, and a beneficiary of a plan that has not released all receive the same 404.

Limits

The event chain

Each plan carries an append-only chain. Event n hashes as

hash = sha256(prev_hash | plan_id | seq | kind | created_at)

with prev_hash empty for seq = 1 and | a literal pipe. Kinds are created, checkin, content_updated, warning, attested, triggered, released, claimed, cancelled. GET /v1/legacy/plans/{id} returns the ordered events with each one's anchor (null until the anchor worker has published it; then the anchor id, its status, and the Solana signature). The chain is content-free by construction — nothing derived from the note or the files enters it — so publishing it reveals only that a plan existed and when it moved.

Webhooks

All content-free. Subscribe on the webhooks page.

EventDelivered toPayload
legacy_warningOwnerplan_id, plan_name, warned_at, grace_secs, attesters_required, release_not_before
legacy_releasedOwnerplan_id, plan_name, beneficiaries (a count)
legacy_claim_availableEach beneficiary wallet that maps to a Sable accountplan_id, plan_name, wallet
legacy_cancelledOwnerplan_id, plan_name, cancelled_at

legacy_warning is the one to route somewhere loud: it is the signal that you, the owner, have a grace window in which to check in.

Honest limitations