API reference
Placement
POST /prepare-write — get the providers, contracts, and access tokens for writing a slab's shards.
prepare-write is the first call of every upload. It returns a set of providers with funded
contracts and an access token for each, ready to receive shards.
POST /prepare-write
curl -X POST "$TESSERA_API/prepare-write?$AUTH" \
-H 'content-type: application/json' \
-d '{"totalShards": 15, "minShards": 10}'
Request body
| Field | Type | Default | Description |
|---|---|---|---|
totalShards | integer | 15 | How many providers you want, one shard each. |
minShards | integer | 10 | How many shards must survive to reconstruct. |
Both fields may be omitted; the defaults are the 10-of-15 geometry.
Response — 200 OK
{
"hosts": [
{
"publicKey": "ed25519:9f3c1a4b...",
"address": "provider-1.example:9984",
"accountToken": "{\"hostKey\":\"ed25519:9f3c1a4b…\",\"account\":\"…\",\"validUntil\":1775212800,\"signature\":\"…\"}",
"contractID": "a1b2c3d4..."
}
]
}
| Field | Description |
|---|---|
publicKey | The provider's identity. ed25519: followed by 64 hex characters. Record this in the slab's sectors[].host. |
address | Network address to connect to, host:port. |
accountToken | An opaque, time-limited authorisation for your writes to this provider. Pass it to the transfer protocol unmodified. |
contractID | The storage contract the shard is written under. Record this in sectors[].contractID. |
The response contains exactly totalShards entries, each a different provider. Provider
uniqueness within a slab is enforced here and again when the slab is registered, so a slab can never
end up with two shards in one place.
Geometry constraints
totalShards / minShards is the redundancy factor, and it is validated:
| Rule | Value |
|---|---|
| Minimum redundancy | 1.5× |
| Maximum redundancy | 4.0× |
minShards | must be ≤ totalShards |
| Geometry | Redundancy | Tolerates | Accepted |
|---|---|---|---|
| 10-of-15 | 1.5× | 5 losses | yes — the default |
| 10-of-20 | 2.0× | 10 losses | yes |
| 6-of-12 | 2.0× | 6 losses | yes |
| 10-of-14 | 1.4× | 4 losses | no — below 1.5× |
| 4-of-20 | 5.0× | 16 losses | no — above 4.0× |
Why there is a maximum
The lower bound protects durability. The upper bound exists because past a point you are paying for storage that buys almost nothing: going from 1.5× to 4.0× multiplies your cost by 2.7 for a reduction in an already-small failure probability. If you have a workload that genuinely justifies more, the answer is a second independent copy, not a wider stripe.
Choosing a geometry
Widen totalShards while holding minShards if you want more failure tolerance for the same
reconstruction threshold. Widening minShards too makes each shard smaller and each read touch more
providers, which usually costs you latency for no durability gain.
The default is the right answer for almost everything. Deviate when you can state why.
Errors
| Status | Body | Cause |
|---|---|---|
400 | {"error": "invalid request body: ..."} | Malformed JSON. |
400 | {"error": "minShards must be <= totalShards"} | Self-explanatory. |
400 | {"error": "redundancy 1.400 < 1.5"} | Below the redundancy floor. |
401 | plain text | Signature or credential problem. |
402 | {"error": "storage cap exceeded", "capBytes": …, "incomingBytes": …} | The write would exceed your storage cap. |
405 | method not allowed | Not a POST. |
503 | {"error": "no contracted hosts available"} | No provider pool currently available. Retry with backoff. |
503 | {"error": "contracts manager not configured"} | Placement is unavailable on this deployment. |
503 | {"error": "no hosts with valid tokens available"} | Providers were found but tokens could not be derived. Retry. |
`503` is transient — treat it as such
Provider availability fluctuates. A 503 from this endpoint means try again shortly, not that your
request was wrong. Use exponential backoff with jitter, and do not fail a user-facing upload on the
first one.
Provider failures mid-upload
Some shard writes will fail. This is normal, not exceptional, and your client should treat it as a routine branch rather than an error path:
- Attempt the write to the assigned provider.
- On failure, do not retry indefinitely — call
prepare-writeagain and take a provider you have not used in this slab. - Place the shard there and continue.
- Register the slab once you have
totalShardssuccessful placements.
Which specific providers hold your shards does not matter. That there are totalShards of them, all
distinct, does.
Token lifetime
accountToken is time-limited. For a long upload, request placement in batches close to when you
will use it rather than fetching all tokens up front and working through them slowly. If a token
expires mid-upload, re-request placement for the remaining shards.
GET /hosts
Lists providers currently considered good for upload. Informational — prepare-write already
selects for you, and does it better, because it also accounts for contract state and fill level.
curl "$TESSERA_API/hosts?$AUTH"
[
{
"publicKey": "ed25519:9f3c1a4b...",
"addresses": ["provider-1.example:9984"],
"countryCode": "",
"latitude": 0,
"longitude": 0,
"goodForUpload": true
}
]
Always an array, never null.
Geography fields are not populated
countryCode, latitude, and longitude are present in the response shape but are currently
returned empty or zero — geo-location of providers is not yet wired up. Do not build jurisdiction
logic on these fields. If you need jurisdiction constraints, that is an Enterprise configuration
applied at selection time.