Publisher
The publisher stores a blob on behalf of a client that does not talk to the storage nodes: it
encodes the data, pays the storage and the gas in IOTA with its own account, uploads the
slivers, certifies the blob and sends the Blob object to the address the client chooses. It also
stores quilts.
Base URL: https://publisher.iotastorage.com.
Since the publisher pays with its own IOTA, the public one requires a token
(Authorization: Bearer <token>) and is used by the project. Without it, it answers
401 UNAUTHENTICATED.
To store your own data, use the app with your wallet, or the
reference JavaScript client: you pay in IOTA and the Blob is yours from the start. Anyone can
read the result through the public aggregator.
| Method | Path | Body |
|---|---|---|
PUT | /v1/blobs?<parameters> | The bytes of the blob. |
PUT | /v1/quilts?<parameters> | The files, as multipart/form-data. |
Parameters
| Parameter | Default | Meaning |
|---|---|---|
epochs | 1 | Epochs to store the blob: an integer from 1 to 53. |
deletable=true | Store a deletable blob (the default). | |
permanent=true | Store a permanent blob, which cannot be deleted. Not together with deletable=true. | |
send_object_to | the publisher | IOTA address (0x…) that receives the Blob object. Without it, the Blob stays in the publisher's account. |
Store a blob
curl -X PUT "https://publisher.iotastorage.com/v1/blobs?epochs=5&send_object_to=0x<your address>" \
-H "Authorization: Bearer $TOKEN" \
--upload-file file
The answer describes the new Blob. For example, for a 1 MiB file stored for 5 epochs from
epoch 1:
{
"newlyCreated": {
"blobObject": {
"id": "0x…",
"registeredEpoch": 1,
"blobId": "<blob ID>",
"size": 1048576,
"encodingType": "RS2",
"certifiedEpoch": 1,
"storage": {
"id": "0x…",
"startEpoch": 1,
"endEpoch": 6,
"storageSize": 5309400
},
"deletable": true
},
"resourceOperation": {
"registerFromScratch": { "encodedLength": 5309400, "epochsAhead": 5 }
},
"cost": 42000
}
}
| Field | Meaning |
|---|---|
blobObject.id | ID of the Blob object on IOTA. |
blobObject.blobId | The blob ID, to read it through the aggregator. |
blobObject.size | Size of the original data, in bytes. |
blobObject.storage | The Storage resource: epochs and encoded size reserved. |
resourceOperation.registerFromScratch.encodedLength | Encoded size that was paid for. |
cost | What the contract charged for storage, in nanos of IOTA (gas not included). |
Store a quilt
PUT /v1/quilts stores several files as one quilt. The body
is multipart/form-data:
- each field is a file, and the field name is its identifier in the quilt (it cannot start with
_); - an optional
_metadatafield holds a JSON list of{ "identifier", "tags" }with text tags for some of the files.
curl -X PUT "https://publisher.iotastorage.com/v1/quilts?epochs=5" \
-H "Authorization: Bearer $TOKEN" \
-F "index.html=@index.html" \
-F "logo.svg=@logo.svg" \
-F '_metadata=[{"identifier": "logo.svg", "tags": {"kind": "image"}}]'
{
"blobStoreResult": {
"newlyCreated": { "blobObject": { … }, "resourceOperation": { … }, "cost": … }
},
"storedQuiltBlobs": [
{ "identifier": "index.html", "quiltPatchId": "<quilt patch ID>" },
{ "identifier": "logo.svg", "quiltPatchId": "<quilt patch ID>" }
]
}
Each file can then be read through the aggregator by its quilt patch ID. With 100 shards a quilt holds up to 66 files.
Limits and behavior
- At most 10 MiB per request (blob or whole quilt body); the network accepts blobs up to about 142 MiB through other clients. Empty blobs are rejected.
- Writes are processed one at a time, since they all pay with the publisher's coins.
- There is no
alreadyCertifiedanswer: everyPUTregisters a newBlob, even for data that is already stored. - CORS is enabled, with the
authorizationandcontent-typerequest headers allowed.
Errors
Same JSON format as the aggregator:
| Code | status | When |
|---|---|---|
| 400 | INVALID_ARGUMENT | Invalid epochs, permanent together with deletable, invalid send_object_to, empty blob, malformed multipart body or _metadata. |
| 401 | UNAUTHENTICATED | Missing or wrong token. |
| 405 | METHOD_NOT_ALLOWED | Wrong method, or a server that only reads. |
| 413 | PAYLOAD_TOO_LARGE | Larger than the publisher's limit or the network's largest blob. |