Skip to content

Read a held document back.

GET
/v1/api/custody/plaintext/{id}
curl --request GET \
--url https://testnet.permafrost.live/v1/api/custody/plaintext/3f2a9c18-5b7e-4d61-9a0c-8e2f1d4b6a37 \
--header 'Authorization: Bearer <token>'

Returns the document itself while its key lives. The served bytes are checked against the fingerprint recorded at store time before they leave the instance.

Once the key has been destroyed this answers 410 and there is no second path to the bytes: the stored ciphertext is still where it was, and nothing on this instance can turn it back into the document.

The response carries the stored-byte hardening headers every read path on this instance carries — an attachment disposition, a default-src 'none'; sandbox content-security policy, nosniff, X-Frame-Options: DENY and Referrer-Policy: no-referrer.

id
required
string format: uuid
Example
3f2a9c18-5b7e-4d61-9a0c-8e2f1d4b6a37

The custody object identifier returned by POST /v1/api/custody/store.

The document.

Media typetext/plain
string
Example
The document to be held.

No tenant on the request. Through the public edge this is the edge’s own refusal of the credential; the custody surface’s own generic refusal has the same status and carries no hint about which header, which surface, or whether the id exists.

Media typeapplication/json

The refusal shape. detail is present on a few routes and deliberately absent from the custody surface, where a detail string could carry an internal path.

object
error
required

The code, or a short fixed sentence.

string
detail

A human-readable note, where a route carries one.

string
Example
{
"error": "unauthorized"
}

No such custody object, an object owned by another tenant, or an object with no owner recorded. One answer for all three.

Media typeapplication/json

The refusal shape. detail is present on a few routes and deliberately absent from the custody surface, where a detail string could carry an internal path.

object
error
required

The code, or a short fixed sentence.

string
detail

A human-readable note, where a route carries one.

string
Example
{
"error": "not found"
}

The key for this object was destroyed. The document cannot be served again, by this route or any other.

Media typeapplication/json

The refusal shape. detail is present on a few routes and deliberately absent from the custody surface, where a detail string could carry an internal path.

object
error
required

The code, or a short fixed sentence.

string
detail

A human-readable note, where a route carries one.

string
Example
{
"error": "plaintext_unrecoverable"
}

The tenant is over an allowance. Retry-After carries whole seconds, rounded up and never zero. The per-tenant refusal also carries retry_after_ms; the custody surface’s own local limiter carries the code alone. Both are per tenant, keyed by the tenant address — one tenant spending its allowance does not consume another’s.

Media typeapplication/json
object
error
required
string
retry_after_ms

How long until one token is available, in milliseconds. Present on the per-tenant refusal.

integer
>= 1
Example
{
"error": "rate_limited",
"retry_after_ms": 240
}
Retry-After
integer
>= 1
Example
1

Whole seconds to wait before retrying.

The stored bytes could not be fetched or decrypted, or failed their integrity check.

Media typeapplication/json

The refusal shape. detail is present on a few routes and deliberately absent from the custody surface, where a detail string could carry an internal path.

object
error
required

The code, or a short fixed sentence.

string
detail

A human-readable note, where a route carries one.

string
Example
{
"error": "decrypt/serve failed"
}

Permafrost runs on Sui testnet and Walrus testnet. Everything here describes a shipped testnet instance, not a production service.