Skip to content

Certification status for one object.

GET
/v1/api/meta/objects/{id}
curl --request GET \
--url https://testnet.permafrost.live/v1/api/meta/objects/42 \
--header 'Authorization: Bearer <token>'

A 404 here means “not yours, or not there”, and the two are one answer. An id owned by another tenant is refused exactly as an id that was never stored, so this route cannot be used to find out what other tenants hold.

Read state to know where the object stands:

State Meaning
PENDING Accepted. Certification has not started.
UPLOADING Certification in flight.
SYNCED Certified on Walrus; the blob id is known.
FAILED Certification ended in an error.
DELETED Marked deleted. Treat it as absent.

blob_id, walrus_epoch and synced_at are null until the object reaches SYNCED.

id
required
integer format: int64
>= 1
Example
42

The object identifier returned at upload time.

The object’s certification metadata.

Media typeapplication/json

The eight fields the /v1/ contract exposes for a stored object. Internal columns are not part of this shape.

object
id
required

The object identifier.

integer format: int64
state
required

Where the object stands. SYNCED is the state in which it has cleared certification on Walrus and its blob id is known.

string
Allowed values: PENDING UPLOADING SYNCED FAILED DELETED
blob_id

The Walrus blob identifier. null until the object reaches SYNCED.

string | null
walrus_epoch

The Walrus epoch at which certification was recorded. null until SYNCED.

integer | null format: int64
synced_at

When the object reached SYNCED, in UTC. null before that.

string | null format: date-time
created_at
required

When the object was first accepted, in UTC.

string format: date-time
size_bytes
required

Object size in bytes.

integer format: int64
content_type
required

The type recorded at upload time.

string
Examples

Certified — the blob id is known.

{
"id": 42,
"state": "SYNCED",
"blob_id": "9c4f3b2a1d0e8f7c6b5a4d3e2f1c0b9a8d7e6f5c4b3a2918",
"walrus_epoch": 137,
"synced_at": "2026-09-13T14:08:21Z",
"created_at": "2026-09-13T14:07:55Z",
"size_bytes": 1048576,
"content_type": "image/png"
}

The credential was not accepted. Every cause answers this same status with this same body — absent, malformed, unknown key, revoked key, unconfigured issuer, bad signature, wrong audience, expired token, or a subject that is not a canonical Sui address. The refusal is made at the edge, before the request reaches a service.

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": "Invalid or inactive API key"
}

The credential is real but the tenant it names has been revoked. Revoking a tenant closes both lanes at once — key and token.

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": "Tenant inactive or not found"
}

No such object, or an object the calling tenant does not own. The two are deliberately one answer, so this route cannot be used to learn what other tenants hold.

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",
"detail": "Object 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.

Something failed on the instance. The reason is logged there, not returned.

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": "internal_error"
}

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