Skip to content

Destroy the key for a held document, and certify it on Sui.

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

This does not come back. The key is destroyed first, and only then is the certificate minted — so a failure to mint never leaves a key alive behind a response that said it was gone.

The answer is the same shape status returns. Read certificate_status to know what landed:

Value Meaning
minted The key is destroyed and the certificate exists. Its object id and transaction digest are in the body.
not_minted The key is destroyed and no certificate landed. The destruction is no less final.

Calling it again on an object already deleted is safe: it returns the existing receipt and destroys nothing twice.

This route draws on a separate, tighter allowance than the rest of the surface, because it is irreversible. A short burst is allowed; a runaway client exhausts the budget long before it gets through a meaningful number of objects.

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

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

The key is destroyed. The body says whether a certificate landed.

Media typeapplication/json

The custody view of one held document. No key, key identifier, custody backend or owner appears here.

object
id
required
string format: uuid
content_hash

Sha256 of the plaintext — the hash the certificate names.

string | null
stored_hash

Sha256 of the stored ciphertext — the hash the verify route indexes.

string | null
alive
required

True while the key lives and the document can still be served.

boolean | null
incinerated

The complement of alive. True once the key has been destroyed.

boolean | null
incinerated_at

When the key was destroyed, in UTC. null while it lives.

string | null format: date-time
certificate_object_id

The on-chain certificate’s object id, once one has been minted.

string | null
certificate_tx_digest

The transaction that minted the certificate.

string | null
certificate_status
required

minted once the certificate exists. not_minted when the key was destroyed and no certificate landed — the destruction is no less final for it.

string
Allowed values: minted not_minted
deletion_mode

The claim the certificate makes, recorded on chain verbatim: auditable_vendor_side. null while the document is alive.

string | null
Example
{
"id": "3f2a9c18-5b7e-4d61-9a0c-8e2f1d4b6a37",
"content_hash": "7d1a2b3c4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
"stored_hash": "0c9b8a7d6e5f40312213a4b5c6d7e8f90a1b2c3d4e5f6071829304b5c6d7e8fa",
"alive": false,
"incinerated": true,
"incinerated_at": "2026-09-13T15:02:44Z",
"certificate_object_id": "0xb4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3",
"certificate_tx_digest": "8xJ2kQ9mVb3TcYw6RfN1sLpA4dZhG7uEoX5nK0iMrQvB",
"certificate_status": "minted",
"deletion_mode": "auditable_vendor_side"
}

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 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.

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