Read a held document back.
const url = 'https://testnet.permafrost.live/v1/api/custody/plaintext/3f2a9c18-5b7e-4d61-9a0c-8e2f1d4b6a37';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Example
3f2a9c18-5b7e-4d61-9a0c-8e2f1d4b6a37The custody object identifier returned by POST /v1/api/custody/store.
Responses
Section titled “Responses”The document.
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.
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
The code, or a short fixed sentence.
A human-readable note, where a route carries one.
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.
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
The code, or a short fixed sentence.
A human-readable note, where a route carries one.
Example
{ "error": "not found"}The key for this object was destroyed. The document cannot be served again, by this route or any other.
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
The code, or a short fixed sentence.
A human-readable note, where a route carries one.
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.
object
How long until one token is available, in milliseconds. Present on the per-tenant refusal.
Example
{ "error": "rate_limited", "retry_after_ms": 240}Headers
Section titled “Headers”Example
1Whole seconds to wait before retrying.
The stored bytes could not be fetched or decrypted, or failed their integrity check.
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
The code, or a short fixed sentence.
A human-readable note, where a route carries one.
Example
{ "error": "decrypt/serve failed"}Permafrost runs on Sui testnet and Walrus testnet. Everything here describes a shipped testnet instance, not a production service.